Layoutendiannessbyte orderserializationprotocolsportability

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.

Follow the mechanism

Software view, hardware view

The gap between what you wrote and what the machine does is where this whole domain lives.

The question
In what order does a multi-byte value actually sit in memory, and when does that order become my problem?
What you wrote
An integer is a number. Writing it to a file or a socket writes the number.
What the hardware does
An integer is a sequence of bytes at consecutive addresses, and the mapping from numeric significance to address order is a property of the architecture. Writing it out writes those bytes in address order.
Every binary format and every network protocol has to pick an order and state it. Code that ignores this works perfectly until it talks to a machine that chose differently, at which point values arrive byte-reversed and nothing about the source hints at why.
SourceCompilerInstructionsFront EndExecutionRegistersCachesMemoryI/OBehavior

The same value, two byte orders

ISA-SPECIFICx86-64 is little-endian. AArch64 is bi-endian but overwhelmingly run little-endian. Network protocols conventionally use big-endian regardless of host architecture.

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.

The 32-bit value 0x12345678 in memory, both ways
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.

Where endianness does and does not matter
SituationDoes it matter?Why
Arithmetic on integers in memoryNoBoth ends use the same convention; it cancels
Writing an integer to a network socketYesThe peer may be a different architecture
Writing an integer to a binary fileYesThe reader may be a different architecture, or a future you
Casting a byte buffer to an int pointerYesAlso an Alignment: Why Addresses Are Not Arbitrary hazard and undefined behaviour
Memory-mapped device registersYesThe device defines the order, not the CPU
Text formats such as JSONNoNumbers are encoded as characters, not raw bytes
Single-byte valuesNoThere 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.

Explicit conversion is portable; casting is not
1// FRAGILE: depends on host endianness, and is an
2// alignment hazard and undefined behaviour besides
3memcpy(buffer, &value, 4) // writes host order, whatever that is
4
5// PORTABLE: produces big-endian bytes on any host
6buffer[0] = (value >> 24) & 0xFF
7buffer[1] = (value >> 16) & 0xFF
8buffer[2] = (value >> 8) & 0xFF
9buffer[3] = value & 0xFF
10
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 a
18// 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.

  1. 1
    Value → register: the integer is a number in a register with no byte order of its own.
  2. 2
    Register → memory: the store unit writes bytes to consecutive addresses in the architecture's defined order.
  3. 3
    Memory → buffer: a raw copy preserves that host-specific order into the outgoing byte stream.
  4. 4
    Buffer → network: bytes travel in address order, carrying the host convention with them.
  5. 5
    Peer → value: the receiver reassembles bytes using its own convention, producing a reversed value if the two disagree.
What people conclude from this — wrongly
  • "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

What it causes
  • • 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.
What you can do
  • • 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.
How to see it
  • • 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.
What it costs
  • • 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.

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.

Misconceptions

Claim
“Little-endian is the correct or modern order.”
Reality
Neither is more correct. Little-endian simplifies narrowing conversions; big-endian matches written notation and dominates network protocols. Both are in active use.
Claim
“Endianness affects performance.”
Reality
Conversion is typically a single byte-swap instruction. The cost is a correctness burden at boundaries, not a runtime one.
Claim
“If I always use the same language, I am safe.”
Reality
The language does not determine host byte order. The same program compiled for a different target produces different bytes on the wire.