How to Determine Machine Endianness in C

This article explains how to detect a system's endianness at runtime using the C programming language. Endianness defines the order in which multi-byte data types are arranged in computer memory, differentiating systems between big-endian and little-endian architectures. By using simple techniques such as pointer-based byte casting or memory-shared unions, developers can quickly inspect how multi-byte integers are laid out in RAM, ensuring portability across network protocols, file serialization, and low-level hardware interfaces.

Understanding Endianness

In a computer's memory, multi-byte values are stored across contiguous byte addresses:

  • Little-Endian: The least significant byte (LSB) is stored at the lowest memory address. Modern x86 and ARM processors typically operate in little-endian mode.
  • Big-Endian: The most significant byte (MSB) is stored at the lowest memory address. This matches network byte order and is used by older architectures such as SPARC and PowerPC.

Consider a 32-bit integer with the hexadecimal value 0x00000001. In memory, the bytes are arranged as follows:

Address Offset Little-Endian Big-Endian
0 (Lowest address) 0x01 0x00
1 0x00 0x00
2 0x00 0x00
3 (Highest address) 0x00 0x01

Because the byte at offset 0 holds different values depending on the architecture, checking the value at that address identifies the system's endianness.

Method 1: Byte-Casting with a Pointer

The byte-cast approach works by assigning an integer the value 1, obtaining its memory address, and casting that address to a pointer to an unsigned char. Because the C standard allows any data type to be aliased by a character pointer, this method does not violate strict aliasing rules.

#include <stdio.h>
#include <stdbool.h>

bool is_little_endian_pointer(void) {
    unsigned int value = 1;
    unsigned char *byte_ptr = (unsigned char *)&value;

    // If the lowest address contains 1, the machine is little-endian.
    return (*byte_ptr == 1);
}

int main(void) {
    if (is_little_endian_pointer()) {
        printf("Architecture: Little-Endian\n");
    } else {
        printf("Architecture: Big-Endian\n");
    }
    return 0;
}

When *byte_ptr is evaluated, the program reads only the first byte of value. If *byte_ptr evaluates to 1, the LSB is stored at the lowest memory address, confirming a little-endian host. If it evaluates to 0, the host is big-endian.

Method 2: Using a Union

A union in C allows multiple variables to occupy the same memory location. By pairing a multi-byte integer with an array of bytes, you can assign a value to the integer and immediately read the individual bytes without manual pointer arithmetic.

#include <stdio.h>
#include <stdbool.h>

bool is_little_endian_union(void) {
    union {
        unsigned int integer_val;
        unsigned char byte_val[sizeof(unsigned int)];
    } test_union;

    test_union.integer_val = 1;

    // Check the first byte in the memory block
    return (test_union.byte_val[0] == 1);
}

int main(void) {
    if (is_little_endian_union()) {
        printf("Architecture: Little-Endian\n");
    } else {
        printf("Architecture: Big-Endian\n");
    }
    return 0;
}

In the union approach, test_union.integer_val and test_union.byte_val begin at the exact same base address. Reading test_union.byte_val[0] provides the byte stored at that first address.

Summary

Both methods execute in constant time \(O(1)\) and require minimal overhead. The pointer-casting method is generally favored for its brevity and inline efficiency, while the union method clearly defines memory sharing through C type definitions. Both implementations accurately report the byte ordering of the underlying host machine.