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?