Binary, Hex, and the Byte as the Unit of Access
What This Concept Is
Memory is not a sea of bits. On every mainstream machine, memory is addressable in bytes, where one byte is eight bits. Every address in C names one byte. Hex is just a compact spelling of those bytes: one hex digit is exactly four bits (a nibble), so one byte is exactly two hex digits.
Three facts do most of the work:
1 byte = 8 bits = 2 hex digits, valued0x00to0xFF, which is0to255unsigned- a hex literal
0xABis shorthand for the bit pattern1010 1011 - when you take
&x, you get the address of the first byte ofx;sizeof(x)counts bytes
Everything that follows (signed integers, floats, pointers, structs) is one or more consecutive bytes at some address, interpreted according to a type.
Why It Matters Here
If the byte is not your unit of reasoning, you will miscount in every later concept:
- pointer arithmetic moves in bytes, scaled by type size
- struct layout inserts padding measured in bytes
memcpy,read, andwriteall measure in bytes- debuggers, hex dumps, and network protocols all speak hex
Decimal is for humans and for values. Hex is for bit patterns, addresses, and machine-level reasoning.
Concrete Example
The 32-bit integer 0x12345678 in memory on a typical little-endian machine occupies four consecutive bytes:
Address: 0x1000 0x1001 0x1002 0x1003
Byte: 0x78 0x56 0x34 0x12
Reading the bytes from low address to high address gives 78 56 34 12. Reassembling them most-significant-byte-first recovers 0x12345678. That reversed-looking byte order is little-endian; we return to it in Cluster 5.
Common Confusion / Misconception
"Bits and bytes are interchangeable units." They are not. A 32-bit integer is 4 bytes, not 32 bytes. A 1 GB file is 10^9 bytes (8 * 10^9 bits). Memory bandwidth and network speeds are often quoted in bits per second, while file sizes and memory sizes are in bytes. Misreading one as the other is an eight-fold error.
"Hex is a different number system." It is the same numbers in a different base. 0xFF = 255, 0x10 = 16, 0x100 = 256. Base 16 is convenient because 2 hex digits cover exactly one byte.
How To Use It
For every byte-sized reasoning task:
- Write the value in hex first.
-1as a 32-bit signed integer is0xFFFFFFFF, not the decimal-1. - Group bits into nibbles.
0b1010 1011 = 0xAB. - When printing for debugging, prefer
%x,%#x, orprintf("%02x ", byte)in a loop. - When an address is in a register or variable, think of it as "the index of a byte," not a raw integer.
Check Yourself
- How many bytes does
0x1A2B3C4Doccupy? How many hex digits? How many bits? - Why is
0xFFnot the same as-1, even though both are sometimes printed as255with%u? - Convert
0b1100 0010 1010 0001to hex. - An address is printed as
0x7ffee5a02c40. How many bytes does this pointer refer to as an address, and how many bytes does the object it points at likely occupy if it is adouble? - You read
0x01 0x00 0x00 0x00as the first four bytes of a file. If the file is a little-endianuint32_t, what value is it? If it is big-endian, what value is it?
Design Note: Why Bytes and Not Bits?
Machines could be bit-addressable: every individual bit could have its own address. They are not. Memory is byte-addressable because:
- Hardware pins and bus widths are measured in bytes, not bits. A 64-bit bus moves 8 bytes per cycle.
- Most useful data (characters, small integers) needs more than one bit. Giving each byte its own address is the right granularity for indexing.
- Aligning objects on power-of-two byte boundaries is cheap in hardware and makes struct layout (Cluster 5) predictable.
When you do need individual bits -- flags, bit fields, compression -- you use bitwise operators on a byte (or word), not bit addresses. That is the topic of Concept 15.
Mini Drill or Application
Compile and run:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint32_t x = 0x12345678;
unsigned char *p = (unsigned char *)&x;
for (size_t i = 0; i < sizeof x; i++) printf("%02x ", p[i]);
putchar('\n');
return 0;
}
Build: gcc -Wall -Wextra -o hex_dump hex_dump.c
Predict the output before running. Now change the type to uint16_t and the value to 0xABCD. Predict again.