A 32-bit integer occupies four bytes of memory. Endianness is the order those bytes are stored in. It doesn’t change the value, and it doesn’t affect bitwise operators. It matters only when you read or write raw bytes: files, network packets, memory dumps, and binary protocols. Getting it wrong produces values that look random but are actually the right bytes in the wrong order.

Big-Endian and Little-Endian

Take the value 0x12345678. Its most significant byte is 0x12 and its least significant byte is 0x78.

AddressBig-endianLittle-endian
+01278
+13456
+25634
+37812
  • Big-endian stores the most significant byte first, the same order we write numbers.
  • Little-endian stores the least significant byte first.

The names come from Gulliver’s Travels, where two factions argue about which end of a boiled egg to break. The joke is that neither choice is better. Both work, but mixing them causes trouble.

Who Uses Which

  • Little-endian: x86 and x86-64 (Intel, AMD), and in practice almost all ARM systems, including phones and Apple silicon Macs. Most computers you use today are little-endian.
  • Big-endian: network protocols (TCP/IP headers use “network byte order”), many file formats such as PNG and JPEG headers, Java’s DataInputStream, and some older or specialised processors.
  • Configurable: ARM and some other architectures can run in either mode.

Little-endian has a small practical advantage: the low byte of a value is at the same address regardless of its width, so reading a 32-bit value as 8 or 16 bits gives the low part directly.

Endianness Does Not Affect Bitwise Operators

This point confuses many people. Operators such as &, |, <<, and >> work on values in registers, not on bytes in memory:

uint32_t x = 0x12345678;
uint8_t low = x & 0xFF;        /* always 0x78, on every CPU */
uint8_t top = x >> 24;         /* always 0x12, on every CPU */

Code that extracts bytes with shifts and masks is endian-independent. Endianness only matters when you reinterpret memory directly, for example by casting a pointer:

uint32_t x = 0x12345678;
uint8_t *p = (uint8_t *)&x;
printf("%02X\n", p[0]);        /* 78 on little-endian, 12 on big-endian */

Swapping Bytes With Shifts and Masks

To reverse the byte order of a 32-bit value, move each byte to its mirrored position:

uint32_t bswap32(uint32_t x) {
    return ((x & 0x000000FFu) << 24) |
           ((x & 0x0000FF00u) << 8)  |
           ((x & 0x00FF0000u) >> 8)  |
           ((x & 0xFF000000u) >> 24);
}
/* bswap32(0x12345678) == 0x78563412 */

Each line isolates one byte with AND and shifts it into place, and OR combines the four. Compilers recognise this pattern and emit a single bswap instruction. In practice, use the built-ins: __builtin_bswap32 in GCC and Clang, _byteswap_ulong in MSVC, or htonl() and ntohl() for network byte order. You can trace each step in the Bitwise Hex Calculator.

Reading Binary Data Safely

The robust approach is to state the byte order explicitly whenever you convert between bytes and integers.

Python:

import struct

data = bytes([0x12, 0x34, 0x56, 0x78])
int.from_bytes(data, "big")      # 305419896 (0x12345678)
int.from_bytes(data, "little")   # 2018915346 (0x78563412)

struct.unpack(">I", data)[0]     # big-endian unsigned 32-bit
struct.unpack("<I", data)[0]     # little-endian
(0x12345678).to_bytes(4, "little")  # b'xV4\x12'

JavaScript:

const buf = new Uint8Array([0x12, 0x34, 0x56, 0x78]).buffer;
const view = new DataView(buf);
view.getUint32(0, false).toString(16);  // "12345678"  big-endian
view.getUint32(0, true).toString(16);   // "78563412"  little-endian

DataView defaults to big-endian when the second argument is omitted, which surprises people on little-endian machines. Always pass it.

C, portable version:

uint32_t read_be32(const uint8_t *b) {
    return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) |
           ((uint32_t)b[2] << 8)  |  (uint32_t)b[3];
}

Assembling the value from bytes with shifts and OR works the same on every CPU, so no #ifdef is needed.

Spotting Endianness Bugs

Symptoms that suggest a byte-order mix-up:

  • A port number like 80 shows up as 20480 (0x0050 read as 0x5000).
  • A length field is absurdly large, such as 16777216 instead of 1.
  • An IP address prints reversed, for example 10.1.168.192 instead of 192.168.1.10.

When you see one of these, print the raw bytes in hex and compare with the expected value byte by byte.

Bit Order Is a Separate Question

Endianness is about byte order. Bit numbering within a byte is a separate convention: most documentation calls the least significant bit “bit 0”, but some (notably older IBM and some networking standards) number from the most significant end. Serial links may also transmit bits LSB-first or MSB-first. Always check the datasheet or specification.

Summary

Endianness describes how multi-byte values are laid out in memory. Little-endian dominates CPUs and big-endian dominates network protocols and many file formats. Bitwise operators are unaffected because they work on values. Specify the byte order whenever you convert bytes to integers, and use shifts and masks, as in Binary, Decimal and Hex Conversion, when you need code that works on any machine.