My previous understanding is that all information was stored in a binary form (0s and 1s) on hard drives, and in blocks of 8-bits, in modern computer hard drives. And that hexadecimal is utilized to facilitate the display of information, so humans aren't required to read through long blocks of bits.
Your previous understanding is exactly correct, and I have a feeling you already understand the rest of this answer, but I want to explain a few ideas people often conflate anyways. I'm going to try to be as brief as possible, but it will be tough.
Bytes, Storage
Data is typically stored on a hard drive (or in memory) in 8-bit blocks called bytes. A single bit has two possible values, which, by convention, we refer to as 0 and 1. A single byte therefore has 28 = 256 possible values.
I don't actually know why 8-bit blocks are the typical unit. I'm not familiar enough with the history of computer development to know that, but I can at least tell you that we continue to use 8-bit bytes on common systems because we're kind of locked into it at this point and there's no reason to change.
Also, because I know this will come up, in reality data isn't necessarily stored on a drive in one-byte blocks or one byte at a time. Typical hard drives often use larger blocks, etc. However, for the scope of your question, none of this matters. All that matters is that it appears to us that hard drives operate on individual bytes. The actual implementation is an interesting topic but doesn't affect us here: traditionally, humans generally discuss storage in terms of individual bytes, and we probably are human.
Binary, Hexadecimal
The reason we often use binary notation when discussing values of bit-related things like bytes is simply because it makes the most sense. Since a bit has two possible values, this naturally translates to a binary representation of numbers (binary meaning each digit has two possible values, as opposed to the decimal system we typically use every day, where each digit has ten possible values).
The reason us programmers also like to use hexadecimal (each digit has sixteen possible values) notation is because it's really convenient. It just so happens that the range representable by a single hexadecimal digit corresponds exactly to the range representable by four binary digits. And this fits nicely into our 8-bit bytes: two hex digits can represent every value of a byte. It's also a manageable system for our brains, it's really easy to relate hex to binary once you get used to it.
We could have used a base-256 system in writing, but that would be inconvenient, because it's hard to come up with 256 easily typeable, speakable, and memorizable characters. We could have used a base-17 system but that doesn't correspond as neatly to 8-digit binary numbers. So we use hexadecimal, because it makes a ton of sense for us.
Text
We use text a lot, so it's to our benefit to come up with standard ways of representing the characters we use every day as series of bytes. This mapping of characters to bytes is called a "character encoding" or "character set". Of course, we suck at actually agreeing on things, and also many different such mappings were developed independently for many different needs, so we have many character sets, like ASCII, or ISO-8859-1, or JIS.
As an aside, unicode was invented to attempt to define a standard that made everybody happy, unifying all of our various character encodings, hence the name "unicode".
But the point is, text is represented by series of bytes, and exactly what each series of bytes means is determined by various character encodings, and the fact that the bytes represent text at all relies on the assumption that the program reading the bytes understands that they're supposed to represent text. ASCII is a convenient one to talk about because each character maps to exactly one byte, and also it's really old, really simple, was really widely used, and despite being grossly inadequate for the global community, is still very popular and easy to discuss.
Semantics
This is, I'm convinced, the most confusing point to many people.
Bytes are just bytes. They have essentially arbitrary values. What those values actually mean is determined only by context and by what a program reading them actually does with them.
For example, recalling that a byte can take on 256 values, the value 97 (binary 01100001, hexadecimal 61), at the end of the day, can mean many different things:
- If the byte is treated as an integer value, it's the number 97.
- If the byte is treated as an ASCII character, it's the letter
a.
- If the byte is treated as a machine instruction for Intel x86 compatible processors, it's the
POPA or POPAD instruction (doesn't matter if you don't know what these are, that's not the point).
- If the byte represents a pixel in a grayscale image, it's probably this shade of gray.
- If the byte is part of some map data for some game, maybe it's a tree or a fence or something.
- Etc.
Even for numeric values the bit patterns can take on different meanings, for example:
- Sometimes we're satisfied with the values 0-255. Other times we want to handle negative numbers so we shift the range of semantic values to -128 thru 127 and use the first bit to indicate if it's negative or not. Or whatever. The sky is the limit (although, like character encodings, there is a generally agreed upon standard set of rules for integer values as well).
- Sometimes due to various circumstances, we even encode integer values in other ways, e.g. BCD.
- Sometimes we need to represent larger integers. So we use many bytes. Even this has options, see "endianness".
- Sometimes we need to represent decimal numbers. Many options here as well, see floating-point and fixed-point for options here.
The point of all this is a byte is just a byte, it means nothing until you have context. If a program writes some bytes with some intended meaning, only a program that reads them and interprets them as having that same meaning will be able to make proper sense of it.
Putting This All Together
So now, relating this all back to your answer, this should actually be really simple now:
- Your friend is referring to the idea of you storing a number as a textual representation of its value in hexadecimal. For example, the value 97, in hex might be 61. This is a two digit number, containing character "6" followed by "1". Encoded as ASCII that would be two bytes: the value 54 followed by the value 49 (decimal). But that only has meaning if, when you read those bytes back, you understand them to be two ASCII encoded hexadecimal digits.
- You could also just store the value 97. That's only one byte. That's half the length of the previous option. But of course, that only has meaning if, when you read that byte back, you understand it as corresponding directly to an integer value.
Typically, us programmers would probably choose the second option, but it really, really depends on context. For example, in an HTML document, which is designed to be human readable text, we'd still store an attribute like width="97". Sure it may take up less space to use some tighter representation here, but then it'd be a pain to write HTML. So it really depends on the context and use case.
I hope at least some of this makes sense.