How the offsetof Macro Works in C
The offsetof macro, defined in
<stddef.h>, is a standard C utility used to determine
the exact byte offset of a specific member from the beginning of a
structure. By leveraging compile-time type casting and address
calculation—or dedicated compiler intrinsics—it calculates the precise
layout of a struct without allocating memory or executing runtime
overhead. This article explains the traditional implementation of
offsetof, breaks down the mechanics behind its memory
arithmetic, and explores how modern compilers ensure its safe
execution.
The Classic Implementation
In traditional C libraries, the offsetof macro is
commonly defined as:
#define offsetof(type, member) ((size_t)&(((type *)0)->member))This compact expression uses a clever pointer trick to calculate field positions at compile time.
Step-by-Step Breakdown
Simulating a Base Address with
(type *)0:
The macro casts the integer literal0to a pointer of the specified struct type (type *). This designates an imaginary instance of the struct that begins at virtual memory address0x00000000.Accessing the Target Field with
->member:
Using the member access operator (->), the expression references the target field (((type *)0)->member). Because the base pointer is positioned at zero, the location of this field corresponds directly to its relative distance from the structure's origin.Retrieving the Address with
&:
The address-of operator (&) obtains the memory address of the specified field. Since the struct base starts at0, the address of the field evaluates mathematically to0 + offset, which is simply the offset itself.Converting to an Integer with
(size_t):
Finally, the resulting pointer address is cast tosize_t, an unsigned integer type guaranteed to hold object sizes and offsets. The resulting number represents the exact byte position within the struct.
Accounting for Padding and Data Alignment
Compilers automatically insert unused padding bytes between structure
fields to satisfy CPU memory alignment requirements. Because the
offsetof macro relies on the compiler's own understanding
of the struct's layout, it accurately accounts for any internal padding
or boundary alignment inserted between fields.
Modern Implementations and Undefined Behavior
Under a strict reading of the C standard, dereferencing a null
pointer (((type *)0)->member) invokes undefined
behavior, even though the & operator prevents actual
memory access at runtime.
To maintain strict standard conformance and support complex types in C++, modern compilers replace the pointer-based macro with built-in primitives. For example, GCC and Clang define the macro using a compiler built-in:
#define offsetof(type, member) __builtin_offsetof(type, member)The built-in intrinsic inspects the internal abstract syntax tree (AST) during compilation to retrieve the byte offset directly, bypassing pointer arithmetic entirely while delivering the exact same result.