size_t结构非数组成员崩溃的强制转换

size_t cast of struct non-array member crashes

本文关键字:崩溃 转换 组成员 数组 结构 size      更新时间:2023-10-16

为什么在下面的代码中...

#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;
}

。第二个size_t操作是否会导致 SIGSEGV,而第一个操作不会?该程序的输出是:

Why does this work? 5
Segmentation fault (core dumped)

有人可以解释一下这行代码实际上在做什么吗?

(size_t) (((struct Foo*) 0)->c)

我以前从未见过这种语法,但对我来说,它看起来像是一对c的转换——这是工作情况下的一个数组(我认为它退化为指针(——到一个size_t。所以我认为代码将指针(即地址(投射到size_t......这似乎是一个无害但毫无意义的操作......但在实践中,在工作情况下,代码不会返回无意义的值,而是可靠地返回似乎是c偏移量的内容。为什么会这样?

(size_t) (((struct Bar*) 0)->c)

首先将等价于 NULL 的数 0 强制转换为 Foo*。您唯一可以对空指针执行的操作就是将其与 NULL 进行比较。

但是,指针被->c非法访问,产生未定义的行为。在这种情况下,程序可能会因 SIGSEGV 而崩溃,也可能不会崩溃。

代码是无稽之谈。也许意思是这样的:

(size_t) &(((struct Bar*) 0)->c)

这也是非法的,但它恰好是实现offsetof宏的老式方法。

碰巧的是,当c是一个数组时,数组到指针的转换有效地插入了一个隐式&运算符,因此您得到的结果等效于 offsetof(Foo, c)

如果结构指针(结构 Foo*(0 的地址为 0x0000,而 c 是其数组的地址,则数组的地址 ((struct Foo*( 0(->c 位于 0x0005。 毕竟,数组的名称表示其地址。

正如 Griffiths 指出的那样,如果 Bar 的临时指针是 0x0000,并且 c 是其字符变量之一的地址,它会尝试从 0x0005 中获取 ((struct Bar*( 0(->c 的值,这显然从未被分配过,从而导致段错误。

它看起来只是在检查偏移量。 如果你要对它进行指针数学运算,最好实际分配结构的实例;或者像potatoswatter指出的那样使用offsetof(Foo,c(。