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

  1. Simulating a Base Address with (type *)0:
    The macro casts the integer literal 0 to a pointer of the specified struct type (type *). This designates an imaginary instance of the struct that begins at virtual memory address 0x00000000.

  2. 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.

  3. Retrieving the Address with &:
    The address-of operator (&) obtains the memory address of the specified field. Since the struct base starts at 0, the address of the field evaluates mathematically to 0 + offset, which is simply the offset itself.

  4. Converting to an Integer with (size_t):
    Finally, the resulting pointer address is cast to size_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.