Lesson 07 / 16
Byte Order
The order in which multi-byte values are laid out in memory, little- and big-endian representation, and the problems this creates when exchanging data.
Contents
Up to this point, how many bits a value has and how those bits are interpreted have
been established. One question remains open: memory is addressed at the byte level,
yet the value 0x41424344 is four bytes. In which order do these four bytes occupy
four consecutive addresses?
The question has no single correct answer. Different families of hardware have given different answers, and this difference leads to silent corruption when data is moved between two systems. This lesson defines the difference and the safeguard taken against it.
Two Layouts
The value’s most significant byte is 0x41, and its least significant byte is
0x44. There are two layout arrangements:
In big-endian order, the most significant byte is written to the lowest address:
| Address | ||||
|---|---|---|---|---|
| Byte | 41 |
42 |
43 |
44 |
In little-endian order, the least significant byte is written to the lowest address:
| Address | ||||
|---|---|---|---|---|
| Byte | 44 |
43 |
42 |
41 |
The value is the same in both orders: . What changes is the physical layout of that value in memory. The names are a reference to the dispute in Gulliver’s Travels over which end an egg should be cracked from; the naming captures well the gap between the technical weight of the question and the practical fatigue it produces.
The Rationale Behind Each Order
The case for little-endian order is that a width conversion does not change the address. On a little-endian system, reading a single byte from the address of a 32-bit value gives that value’s least significant byte; reading two bytes from the same address gives the low sixteen bits. Narrowing a value to a smaller type requires no address arithmetic.
The case for big-endian order is readability and ordering. When a memory dump is read left to right, the bytes appear in the same order the number is written. Multi-byte values compared byte by byte also produce a comparison that matches numeric ordering, which is a convenience for structures that rely on lexicographic key ordering.
Both cases are technically valid, and the choice is largely historical. What matters is that a system’s chosen order is known, and that the order is fixed explicitly whenever data leaves that system.
Same Bytes, Different Values
The concrete form of the problem is this: when the four bytes 41 42 43 44 are read
from a file, which number results?
- Under a big-endian reading:
0x41424344= - Under a little-endian reading:
0x44434241=
The two values differ by more than fifty million, and no error is raised anywhere: the program runs, and its result is wrong. This is the final instance of a principle repeated throughout previous lessons: bytes carry no meaning of their own — meaning comes from the rule used to read them.
The situation differs for text. ASCII and UTF-8 are sequences made of single-byte
units; a byte sequence’s order is determined by the encoding itself, so the byte-order
problem does not arise. The bytes 41 42 43 44 read as UTF-8 give ABCD on every
system.
By contrast, the units of UTF-16 and UTF-32 are two and four bytes; in these encodings, byte order must be stated explicitly. The way it is stated is by writing the code point U+FEFF, the byte order mark, at the start of the text. UTF-8 text does not need the same mark; its presence implies only the encoding, not the order, and it causes unexpected behavior in some parsers.
Network Byte Order
Network protocols must be independent of the layout of the hardware at either end. For this reason, a single order is fixed for multi-byte fields in protocol headers: big-endian. This convention is called network byte order.
The program performs the conversion from host order to network order explicitly. Conversion functions do nothing if the host order is already big-endian; if it is little-endian, they reverse the bytes. Code working correctly on both kinds of hardware depends on this conversion never being skipped.
The same discipline applies to file formats: when a binary format is defined, field widths and byte order are written into the documentation. Writing memory contents directly to disk or to the network as-is produces a file that depends on the hardware that created it.
In Practice
In Python, byte order is stated explicitly to the conversion functions:
import sys, struct print(sys.byteorder) # this system's order: 'little' or 'big' value = 0x41424344 print(value.to_bytes(4, "big").hex()) # 41424344 print(value.to_bytes(4, "little").hex()) # 44434241 raw = b"ABCD" # bytes 41 42 43 44 print(hex(int.from_bytes(raw, "big"))) # 0x41424344 print(hex(int.from_bytes(raw, "little"))) # 0x44434241 print(struct.pack(">I", value)) # b'ABCD' print(struct.pack("<I", value)) # b'DCBA' print(struct.unpack("!I", b"ABCD")[0]) # 1094861636 — '!' is network byte order
The call struct.pack(">I", 0x41424344) producing b'ABCD' ties together every
lesson of this topic in a single line: the same thirty-two bits read as
as an integer, as a floating-point
number, ABCD as text, and, in memory, as the sequence 41 42 43 44 or
44 43 42 41 depending on the system.
The first character of the format string states the order: < little-endian, >
big-endian, ! network byte order (equivalent to big-endian). If this character is
omitted, the running system’s order is used — the situation that portable code must
avoid.
Not to Be Confused with Bit Order
The endianness discussion concerns bytes, not bits. The positional values of the
bits within a byte are the same under both orders: the most significant bit is on the
left, the least significant bit is on the right. On a little-endian system, the byte
0x41 is still the pattern 0100 0001; it is not reversed.
Bit order is a separate concept that applies in a different context: in the hardware layers where data is transmitted serially over a wire, which end the bits are sent from depends on the protocol. This is a decision independent of the byte-level order that software sees, and it is treated in the physical layer topic of the networks curriculum.
A third source of confusion is the memory layout of bit fields. The order in which fields with a specified bit width are packed inside a structure depends on the compiler, in addition to byte order. For this reason bit fields are not used in binary formats that need to be portable; fields are instead produced with explicit masking and shifting — exactly what the operators from the fifth lesson are for.
The Discipline of Data Exchange
The way to avoid the byte-order problem is to handle it explicitly at every boundary:
- Order is documented in binary formats. Field widths and byte order are part of the format definition; both the reader and the writer conform to the same document.
- Conversion happens at boundaries. A value is converted to host order as it enters the system, and to the fixed order as it leaves; the code in between never thinks about order at all.
- Memory contents are not transferred directly. A raw copy of a structure is not portable, due to alignment and padding differences in addition to byte order.
- Text formats do not solve the problem by themselves. Text-based formats such as JSON have no byte-order problem, but numeric precision and encoding decisions must be documented instead.
This discipline will come up again in every layer that works with network protocols and file formats.
Summary
- The bytes of a multi-byte value can be laid out in memory in two different orders: little-endian places the least significant byte at the lowest address, big-endian places the most significant byte there.
- The value stays the same; the layout changes — the same byte sequence gives a different number under the two orders.
- Little-endian order frees width narrowing from address arithmetic; big-endian order gives memory-dump readability and lexicographic ordering.
- Encodings made of single-byte units (ASCII, UTF-8) have no byte-order problem; UTF-16 and UTF-32 state their order with a byte order mark.
- Network protocols fix big-endian order; the conversion is performed explicitly by the program.
- In binary data exchange, byte order is part of the format definition; a raw memory copy is not portable.
Next Step
This topic established how data is represented in memory: bits, integers, real
numbers, text, and byte layout. What comes next is what is done with that data. The
next topic takes up how memory connects to the processor and the steps by which an
instruction is executed in hardware; there, the 0x41424344 pattern will appear once
more, this time possibly as an instruction itself.
To keep your progress and take notes, Log in
My notes
Log in to take notes.