offsetof
is defined like this in stddef.h
:
#define offsetof(type, member) ((size_t)&((type *)0)->member)
Does this invoke undefined behavior due to the dereference of a NULL pointer? If not, why?
offsetof
is defined like this in stddef.h
:
#define offsetof(type, member) ((size_t)&((type *)0)->member)
Does this invoke undefined behavior due to the dereference of a NULL pointer? If not, why?
In normal C code, the behavior of ((size_t)&((type *)0)->member)
is not specified by the C standard:
->
, ((type *)0)->member
designates the lvalue of the member member
of the structure to which (type *)0
points. But ((type *)0)
does not point to a structure, and therefore there is no member this can be the lvalue of.size_t
yields the offset of the member, both because we do not know that (type *)0
yields an address that is actually represented with zero in the implementation’s addressing scheme and because the conversion of a pointer to an integer specified by C 2018 6.3.2.3 6 only tells us the result is implementation-defined, not that it yields the address in any otherwise meaningful form.Were this code in a standard header, such as <stddef.h>
, it is under the control of the C implementation and not the C standard, and so questions about whether it is undefined according to the C standard do not apply. The C standard only says how the standard headers behave when included—an implementation may use any means it chooses to achieve the required effects, whether that is simply defining the behavior of source code that is not fully defined by the C standard or putting source code in an entirely different language in the headers. (In fact, the file stddef.h could be entirely empty or not exist at all, and the compiler could supply its required declarations when it sees #include <stddef.h>
without reading any actual file from disk.)
Leaving aside all the other reasons why it might not be a correct implementation of offsetof
,
#define offsetof(type, member) ((size_t)&((type *)0)->member)
is not appropriate even as part of the implementation because everything in stddef.h
must work correctly in both C and C++, and in C++, the above construct definitely will misbehave in the presence of overloaded operator->
. This is why GCC's stddef.h
switched to using a special intrinsic function called __builtin_offsetof
upwards of fifteen years ago.
Yes, I am saying that if you saw this in some stddef.h
, that stddef.h
is buggy.