简体   繁体   中英

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

If the struct pointer (struct Foo*) 0 has an address of 0x0000, and c is the address of its array, the array's address, ((struct Foo*) 0)->c, is at 0x0005. The name of an array represents its address, after all.

As Griffiths points out, if Bar's temporary pointer is 0x0000, and c is the address of one of its character variables, it tries to fetch the value of ((struct Bar*) 0)->c from 0x0005, which has evidently never been allocated, resulting in the segfault.

It does look to only be checking the offset. Better to actually allocate an instance of the struct, if you are going to do pointer math on it; or use offsetof(Foo, c) as potatoswatter points out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM