I'm looking at the macro offsetof
from <cstddef>
, and saw that a possible implementation is via
#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))
I tried it and indeed it works as expected
#include <iostream>
#define my_offsetof(type, member) ((void*) &(((type*)nullptr)->member))
struct S
{
char x;
short y;
int z;
};
int main()
{
std::cout << my_offsetof(S, x) << '\n';
std::cout << my_offsetof(S, y) << '\n';
std::cout << my_offsetof(S, z) << '\n';
S s;
std::cout << (void*) &((&s)->x) << '\n'; // no more relative offsets
std::cout << (void*) &((&s)->y) << '\n'; // no more relative offsets
std::cout << (void*) &((&s)->z) << '\n'; // no more relative offsets
}
the only modification I've done being that I use a final cast to void*
instead of size_t
, as I want to display the address as a pointer.
My question(s):
- Is the code perfectly legal, i.e. is it legal to "access" a member via a
nullptr
, then take its address? If that's the case, then it seems that&(((type*)nullptr)->member)
computes the address of the member relative to 0, is this indeed the case? (it seems so, as in the last 3 lines I get the offsets relative to the address ofs
). - If I remove the final cast to
(void*)
from the macro definition, I get a segfault. Why? Shouldn't&(((type*)nullptr)->member)
be a pointer of typetype*
, or is the type somehow erased here?