Your real question is really about the difference between
typeof( ((struct Foo*)0)->a ) // Relevant code from the macro.
and
int i = ((struct Foo*)0)->a; // Relevant code from your program.
Let's start by using a valid pointer p
instead of 0
, and ask ourselves what the following two snippets do:
struct Foo s = { 0 };
struct Foo *p = &s;
typeof( p->a )
and
int i = p->a;
In the first case, we are trying to get the type of a member of a structure. The value of p
is irrelevant; the compiler only needs its type. In fact, the result is computed during compilation, before p
is allocated or assigned a value.
In the second case, we are trying to read memory. This memory will be found some location relative to the pointer in p
. Different values of p
will result in different memory locations being read.
So what happens when we use 0
instead of a valid pointer?
In the first case, we never had a valid pointer. Because typeof
is evaluated during compilation, the pointer doesn't even exist when typeof
is evaluated. So that means that the following could conceptually work fine:
typeof( ((struct Foo*)0)->a )
And this brings us to the second case.
int i = ((struct Foo*)0)->a;
0
means NULL
when used a pointer, and may not be zero at all.
This tries to read memory some number of bytes after NULL
. But NULL
isn't an address; it's the lack thereof. The concept of reading the memory from an address relative to NULL
is flawed, meaningless. Since the concept is meaningless, it can't possible work fine.
What does the standard say for typeof( ((struct Foo*)0)->a )
?
I don't know.
What does the standard say for int i = ((struct Foo*)0)->a;
?
The C language doesn't define what happens in that situation. We call this undefined behaviour. The compiler is free to do whatever it wants when it encounters it. Commonly, it results in a protection fault (which results in a SIGSEGV signal on unix systems).
$ gcc -Wall -Wextra -pedantic a.c -o a # OP's program
a.c: In function ‘main’:
a.c:11:11: warning: unused variable ‘abc’ [-Wunused-variable]
str_t *abc = (str_t*)((((str_t*)0)->a)-offsetof(str_t,a));
^~~
$ ./a
Segmentation fault (core dumped)