#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
is very similar to a fairly common definition of the standard offsetof()
macro, defined in <stddef.h>
(in C) or <cstddef>
(in C++).
0
is a null pointer constant. Casting it to TYPE *
yields a null pointer of type TYPE *
. Note that the language doesn't guarantee (or even imply) that a null pointer has the value 0, though it very commonly does.
So (TYPE *)0
is notionally the address of an object of type TYPE
located at whatever address the null pointer points to, and ((TYPE *)0)->ELEMENT))
is the ELEMENT
member of that object.
The &
operator takes the address of this ELEMENT
member, and the cast converts that address to type size_t
.
Now if a null pointer happens to point to address 0, then the (nonexistent) object of type TYPE
object starts at address 0, and the address of the ELEMENT
member of that object is at an address that's offset by some number of bytes from address 0. Assuming that the implementation-defined conversion from TYPE *
to size_t
behaves in a straightforward manner (something else that's not guaranteed by the language), the result of the entire expression is going to be the offset of the ELEMENT
member within an object of type TYPE
.
All this depends on several undefined or unspecified behaviors. On most modern systems, the null pointer is implemented as a pointer to address 0, addresses (pointer values) are represented as if they were integers specifying the index of a particular byte within a monolithic addressing space, and converting from a pointer to an integer of the same size just reinterprets the bits. On a system with such characteristics, the OFFSETOF
macro is likely to work, and the implementation may choose to use a similar definition for the standard offsetof
macro. (Code that's part of the implementation may take advantage of implementation-defined or undefined behavior; it's not required to be portable.)
On systems that don't have these characteristics, this OFFSETOF
macro may not work -- and the implementation must use some other method to implement offsetof
. That's why offsetof
is part of the standard library; it can't be implemented portably, but it can always be implemented in some way for any system. And some implementations use compiler magic, like gcc's __builtin_offsetof
.
In practice, it doesn't make much sense to define your own OFFSETOF
macro like this, since any conforming C or C++ implementation will provide a working offsetof
macro in its standard library.