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.