size_t struct 非数组成员的转换崩溃
size_t cast of struct non-array member crashes
为什么在下面的代码中...
#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
...这似乎是无害但无意义的操作...但实际上,在工作情况下,代码不会 return 一个无意义的值,但相当可靠 returns 似乎是 c
的偏移量。为什么会这样?
(size_t) (((struct Bar*) 0)->c)
首先,数字 0
,相当于 NULL
,被转换为 Foo*
。唯一允许对空指针做的事情是将它与 NULL
.
进行比较
但是,指针被 ->c
非法访问,产生了 未定义的行为 。在这种情况下,程序可能会因 SIGSEGV 而崩溃,也可能不会。
代码是废话。也许是这个意思:
(size_t) &(((struct Bar*) 0)->c)
这也是非法的,但恰好是实现offsetof
宏的老式方法。
碰巧,当 c
是一个数组时,数组到指针 的转换有效地插入了一个隐式的 &
运算符,所以你得到结果相当于 offsetof(Foo, c)
.
如果结构指针(struct Foo*) 0 的地址为0x0000,c 是其数组的地址,则数组的地址((struct Foo*) 0)->c 为0x0005。毕竟,数组的名称代表它的地址。
正如 Griffiths 指出的那样,如果 Bar 的临时指针是 0x0000,并且 c 是其字符变量之一的地址,它会尝试从 0x0005 获取 ((struct Bar*) 0)->c 的值,显然从未分配过,导致段错误。
看起来确实只是在检查偏移量。如果要对其进行指针数学运算,最好实际分配一个结构实例;或使用 offsetof(Foo, c) 正如 potaswatter 指出的那样。
为什么在下面的代码中...
#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
...这似乎是无害但无意义的操作...但实际上,在工作情况下,代码不会 return 一个无意义的值,但相当可靠 returns 似乎是 c
的偏移量。为什么会这样?
(size_t) (((struct Bar*) 0)->c)
首先,数字 0
,相当于 NULL
,被转换为 Foo*
。唯一允许对空指针做的事情是将它与 NULL
.
但是,指针被 ->c
非法访问,产生了 未定义的行为 。在这种情况下,程序可能会因 SIGSEGV 而崩溃,也可能不会。
代码是废话。也许是这个意思:
(size_t) &(((struct Bar*) 0)->c)
这也是非法的,但恰好是实现offsetof
宏的老式方法。
碰巧,当 c
是一个数组时,数组到指针 的转换有效地插入了一个隐式的 &
运算符,所以你得到结果相当于 offsetof(Foo, c)
.
如果结构指针(struct Foo*) 0 的地址为0x0000,c 是其数组的地址,则数组的地址((struct Foo*) 0)->c 为0x0005。毕竟,数组的名称代表它的地址。
正如 Griffiths 指出的那样,如果 Bar 的临时指针是 0x0000,并且 c 是其字符变量之一的地址,它会尝试从 0x0005 获取 ((struct Bar*) 0)->c 的值,显然从未分配过,导致段错误。
看起来确实只是在检查偏移量。如果要对其进行指针数学运算,最好实际分配一个结构实例;或使用 offsetof(Foo, c) 正如 potaswatter 指出的那样。