Endianness: Which Byte Comes First
The value 0x12345678 is unambiguous. The four bytes it occupies in memory are not — their order depends on the machine. It matters exactly when bytes leave the machine, which is why it is a networking and file-format problem more than a CPU one.
Software view, hardware view
The gap between what you wrote and what the machine does is where this whole domain lives.
The same value, two byte orders
Little-endian stores the least significant byte at the lowest address. Big-endian stores the most significant byte first. Neither is more correct; both have coherent arguments. Little-endian makes narrowing conversions trivial, since truncating to a smaller type just reads fewer bytes from the same address. Big-endian matches how humans write numbers and makes hex dumps readable left to right.
Current mainstream desktop and server CPUs are little-endian: x86-64 always, and AArch64 in practice, though the architecture is bi-endian and can be configured otherwise. Big-endian survives in some embedded and legacy systems, and — critically — in most network protocols, where it is conventionally called network byte order.
The dump below is the same 32-bit value under both conventions. Note that the *value* is identical; only the address-to-significance mapping differs.
value: 0x12345678
LITTLE-ENDIAN (x86-64, AArch64 as normally configured)
address: +0 +1 +2 +3
byte: 78 56 34 12
^least significant byte first
BIG-ENDIAN (network byte order, some embedded targets)
address: +0 +1 +2 +3
byte: 12 34 56 78
^most significant byte first
Reading these four bytes as an integer on the wrong machine
gives 0x78563412 -- a plausible-looking number, silently wrong.It only matters at the boundary
Inside a single program on a single machine, endianness is invisible and irrelevant. You store an integer and load an integer; the bytes are written and read by the same convention, so they cancel out. Arithmetic, comparison and assignment never expose it.
It becomes visible the moment bytes cross a boundary where the two ends might disagree: a network socket, a file that another machine will read, a memory-mapped device register, or a memcpy between an integer and a byte buffer. This is why endianness is really a serialisation topic, and why the fix belongs in the serialisation layer rather than scattered through business logic.
The correct discipline is to define the byte order in the format, convert explicitly at the boundary, and never let raw in-memory representations escape. Protocol stacks provide conversion helpers for exactly this reason, and formats that skipped the decision have caused decades of interoperability bugs.
| Situation | Does it matter? | Why |
|---|---|---|
| Arithmetic on integers in memory | No | Both ends use the same convention; it cancels |
| Writing an integer to a network socket | Yes | The peer may be a different architecture |
| Writing an integer to a binary file | Yes | The reader may be a different architecture, or a future you |
| Casting a byte buffer to an int pointer | Yes | Also an Alignment: Why Addresses Are Not Arbitrary hazard and undefined behaviour |
| Memory-mapped device registers | Yes | The device defines the order, not the CPU |
| Text formats such as JSON | No | Numbers are encoded as characters, not raw bytes |
| Single-byte values | No | There is no order to disagree about |
Doing it correctly
The robust pattern is to serialise byte by byte with explicit shifts, which produces the intended order regardless of the host architecture and does not depend on the host being any particular endianness. It compiles down to very little — often a single byte-swap instruction, since architectures provide one precisely for this.
The fragile pattern is casting a struct or an integer pointer into a buffer and copying raw bytes. It works, silently, on every machine that shares your endianness, and fails on the first one that does not. It is also an alignment hazard and undefined behaviour, so it manages to be wrong in three separate ways at once.
One further subtlety worth knowing: endianness applies to multi-byte scalar values, not to arrays or strings. A byte array is written in index order on every machine. Only the internal byte order of individual multi-byte elements is affected.
1// FRAGILE: depends on host endianness, and is an2// alignment hazard and undefined behaviour besides3memcpy(buffer, &value, 4) // writes host order, whatever that is4 5// PORTABLE: produces big-endian bytes on any host6buffer[0] = (value >> 24) & 0xFF7buffer[1] = (value >> 16) & 0xFF8buffer[2] = (value >> 8) & 0xFF9buffer[3] = value & 0xFF10 11// And reading back, again independent of host order:12value = (buffer[0] << 24)13 | (buffer[1] << 16)14 | (buffer[2] << 8)15 | buffer[3]16 17// Compilers commonly recognise both patterns and emit a18// single byte-swap instruction where one exists.Key points
- Endianness is the mapping between byte significance and address order; little-endian puts the least significant byte first.
- It is invisible within one machine and only becomes observable when bytes cross a boundary.
- Network protocols conventionally use big-endian, so hosts must convert regardless of their own order.
- The portable pattern is explicit shift-and-mask serialisation; casting a buffer to a wider pointer is fragile, misaligned and undefined.
- It applies to multi-byte scalars only — byte arrays and strings are written in index order everywhere.
Follow the mechanism
The path through the machine, hop by hop — and the conclusions it invites that are wrong.
- 1Value → register: the integer is a number in a register with no byte order of its own.
- 2Register → memory: the store unit writes bytes to consecutive addresses in the architecture's defined order.
- 3Memory → buffer: a raw copy preserves that host-specific order into the outgoing byte stream.
- 4Buffer → network: bytes travel in address order, carrying the host convention with them.
- 5Peer → value: the receiver reassembles bytes using its own convention, producing a reversed value if the two disagree.
- • "Everything is little-endian now, so it does not matter." Network byte order is big-endian, and file formats and embedded targets still vary.
- • "The bug is data corruption." Byte-reversed values are structurally intact and plausible-looking; the corruption is in interpretation, not storage.
- • "Byte order affects my string handling." It affects multi-byte scalars. Character arrays are written in index order everywhere.
Consequences, controls and cost
- • Binary formats without a declared byte order are silently non-portable and break on first contact with a different architecture.
- • Values arrive byte-reversed rather than obviously corrupt, so bugs look like data errors rather than encoding errors.
- • Hex dumps of memory read differently between architectures, which confuses debugging across platforms.
- • Declare a byte order in every binary format and protocol, and convert explicitly at the serialisation boundary.
- • Use shift-and-mask conversion rather than casting pointers into buffers, so the code is independent of host order.
- • Prefer a serialisation library or a text format when interoperability matters more than raw density.
- • Test against a big-endian target or an emulator if the software must be genuinely portable.
- • Dump the raw bytes of a known value such as `0x12345678` and read the order directly rather than assuming.
- • Compare a serialised payload against the format specification byte for byte at the boundary.
- • Test interoperability against a peer on a different architecture, or a deliberately big-endian test harness.
- • Explicit conversion adds code at every boundary and a small amount of work per value, in exchange for portability.
- • Text formats sidestep the issue entirely but cost size and parsing time relative to packed binary.
Scope
§224 — what these claims are specific to.
- ISA-SPECIFICx86-64 is little-endian; AArch64 is bi-endian but almost always run little-endian; network protocols use big-endian by convention regardless of host.