Search code examples
c++structoffsetsize-t

size_t cast of struct non-array member crashes


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?


Solution

  • (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).