Server rack with blinking network lights, big endian vs little endian byte ordering in networking

Big Endian vs Little Endian: How Byte Order Affects Your Code

Take the number 0x12345678. It is 4 bytes. In memory, those bytes have to go somewhere — but which one goes first? The answer depends on whether your machine is big-endian or little-endian, and getting it wrong silently corrupts your data.

I learned this the hard way while reading a binary file format on an ARM microcontroller. The file was written on x86 (little-endian) but my parser assumed big-endian. Every integer was byteswapped. Took me three hours to realize the bug was one line: I forgot ntohl().

What Endianness Means

Endianness is the order in which bytes are stored in memory for multi-byte values. It does not affect the order of bits within a byte — bits are always numbered from LSB (bit 0) to MSB (bit 7). It is strictly about byte ordering.

ArchitectureEndiannessExample (0x12345678)
x86 / x86-64Little-endian78 56 34 12
ARM (default)Little-endian78 56 34 12
ARM (bi-endian mode)ConfigurableEither
PowerPC (older Macs)Big-endian12 34 56 78
Network byte orderBig-endian12 34 56 78
Java VMBig-endian12 34 56 78

The terms come from Jonathan Swift's Gulliver's Travels, where two factions war over which end of an egg to crack. In computing, it is the same petty argument — but it has real consequences.

Why x86 Chose Little-Endian

Danny Cohen's 1980 paper "On Holy Wars and a Plea for Peace" (the origin of the "endian" term) lays out the arguments. Little-endian has one compelling advantage: you can add multi-byte numbers starting from the lowest address.

// Adding 0x12345678 + 0x00000001 in little-endian memory:
// Address:  00 01 02 03
// Value A:  78 56 34 12
// Value B:  01 00 00 00
//
// The CPU starts at address 00: 0x78 + 0x01 = 0x79
// Carry propagates naturally through addresses 01, 02, 03.
// This is simpler in hardware than big-endian addition.

Another advantage: pointer casting works naturally. In little-endian, if you cast a uint32_t* to a uint8_t*, the byte you read is the least significant byte — which is usually what you want.

uint32_t val = 0x12345678;
uint8_t low_byte = *(uint8_t*)&val;
// Little-endian: low_byte = 0x78 (the LSB)
// Big-endian:    low_byte = 0x12 (the MSB) — surprising!

Detecting Endianness in C

#include <stdint.h>
#include <stdio.h>

int is_little_endian(void) {
    uint16_t x = 0x0001;
    return *(uint8_t*)&x == 0x01;
}

int main() {
    if (is_little_endian())
        printf("Little-endian (x86, ARM default)\n");
    else
        printf("Big-endian (network order, some embedded)\n");
}

When Endianness Bites You

1. Network Programming

TCP/IP uses big-endian (network byte order). Every time you set a port number or IP address in a socket struct, you must convert from host byte order. The functions are htons() (host to network short), htonl() (host to network long), and their inverses ntohs(), ntohl().

struct sockaddr_in addr;
addr.sin_port = htons(8080);           // Must convert!
addr.sin_addr.s_addr = inet_addr("10.0.0.1"); // inet_addr already returns network order

2. Binary File Formats

File formats pick one endianness and stick to it:

3. Cross-Platform Serialization

If you send raw structs between a little-endian x86 server and a big-endian embedded device, your integers will arrive byteswapped. The fix: always serialize to a defined byte order, or use a format like Protobuf/JSON that handles this for you.

Practice with raw bits and hex
Open Bitwise Calculator →