Why in the following code...
#include <iostream>
struct Foo
{
int a;
char b;
char c[1];
};
struct Bar
{
int a;
char b;
char c;
};
int main( int argc, char* argv[] )
{
std::cout << "Why does this work? " << (size_t) (((struct Foo*) 0)->c) << std::endl;
std::cout << "Why does this crash? " << (size_t) (((struct Bar*) 0)->c) << std::endl;
return 0;
}
... does the second size_t
operation cause a SIGSEGV where the first does not? The output of this program is:
Why does this work? 5
Segmentation fault (core dumped)
Can someone also explain what this line of code is actually doing?
(size_t) (((struct Foo*) 0)->c)
I haven't seen this syntax before, but to me it looks like it's a cast of c
- which is an array in the working case (which I think degenerates to a pointer) - to a size_t
. So I'd think the code casts a pointer (i.e. an address) to a size_t
...which seems a harmless but meaningless operation...but in practice, in the working case, the code doesn't return a meaningless value but rather reliably returns what seems to be the offset of c
. Why is this so?
(size_t) (((struct Bar*) 0)->c)
First the number 0
, which is equivalent to NULL
, is casted to a Foo*
. The only thing you're allowed to do with a null pointer is to compare it to NULL
.
However, pointer is illegally accessed by ->c
, producing undefined behavior. In this circumstance, the program may crash with SIGSEGV, or it may not.
The code is nonsense. Perhaps what was meant was this:
(size_t) &(((struct Bar*) 0)->c)
This is also illegal, but it happens to be an old-fashioned way of implementing the offsetof
macro.
As it happens, when c
is an array, the array-to-pointer conversion effectively inserts an implicit &
operator, so you get a result equivalent to offsetof(Foo, c)
.