【问题标题】:uninitialize struct without warning [closed]在没有警告的情况下取消初始化结构[关闭]
【发布时间】:2020-05-06 20:00:15
【问题描述】:

有这个:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

typedef struct {
   int a, b;
} str_t;

int main (void) {

   str_t *abc = (str_t*)((((str_t*)0)->a)-offsetof(str_t,a));

   return 0;
}

我已尝试做与此宏相同的操作:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );}) 

编译器没有给出任何具体的错误,但结果程序崩溃了。为什么宏也不崩溃?

【问题讨论】:

  • 是编译器崩溃了,还是你的程序崩溃了?
  • 你想做什么? ((str_t*)0)-&gt;a只是自找麻烦。
  • ((str_t*)0)-&gt;a 正在访问0 附近的内存。 container_of 宏从不取消引用 0 指针,它只将它用于类型。
  • typeof 里面。这发生在编译时,((type *)0)-&gt;member 的值永远不会被访问。此构造用于获取成员的类型。
  • Re "但你通常会这么说,",不,没有人说编译器崩溃的意思是程序崩溃了。修正了这个问题。 (编译器可能会崩溃;对于像gcc 这样的成熟产品来说,这不太可能。)

标签: c pointers language-lawyer


【解决方案1】:

你真正的问题是关于两者之间的区别

typeof( ((struct Foo*)0)->a )    // Relevant code from the macro.

int i = ((struct Foo*)0)->a;     // Relevant code from your program.

让我们从使用有效指针p 而不是0 开始,然后问自己以下两个sn-ps 做了什么:

struct Foo s = { 0 };
struct Foo *p = &s;

typeof( p->a )

int i = p->a;

在第一种情况下,我们试图获取结构成员的类型。 p 的值无关紧要;编译器只需要它的类型。事实上,结果是在编译过程中计算出来的,在p 被分配或赋值之前。

在第二种情况下,我们正在尝试读取内存。将在相对于p 中的指针的某个位置找到此内存。 p 的不同值将导致读取不同的内存位置。


那么当我们使用0 而不是有效指针时会发生什么?

在第一种情况下,我们从来没有一个有效的指针。因为typeof 在编译期间被评估,所以当typeof 被评估时指针甚至不存在。所以这意味着以下在概念上可以正常工作:

typeof( ((struct Foo*)0)->a )

这将我们带到第二种情况。

int i = ((struct Foo*)0)->a;

0 在使用指针时表示NULL,并且可能根本不为零。

这会尝试在NULL 之后读取一些字节数的内存。但是NULL 不是地址;这是缺乏的。从相对于NULL 的地址读取内存的概念是有缺陷的,毫无意义。由于这个概念没有意义,它不可能正常工作。


标准对typeof( ((struct Foo*)0)-&gt;a ) 有何规定?

我不知道。


标准对int i = ((struct Foo*)0)-&gt;a; 有何规定?

C 语言没有定义在这种情况下会发生什么。我们称之为未定义的行为。编译器在遇到它时可以随意做任何事情。通常,它会导致保护故障(在 unix 系统上会导致 SIGSEGV 信号)。

$ gcc -Wall -Wextra -pedantic a.c -o a     # OP's program
a.c: In function ‘main’:
a.c:11:11: warning: unused variable ‘abc’ [-Wunused-variable]
    str_t *abc = (str_t*)((((str_t*)0)->a)-offsetof(str_t,a));
           ^~~

$ ./a
Segmentation fault (core dumped)

【讨论】:

  • 据此stackoverflow.com/questions/57342141/…((str_t*)0)-&gt;member不为NULL
  • 请再读一遍。链接问题的答案是“C 标准未指定((size_t)&amp;((type *)0)-&gt;member) 的行为”,就像我所做的那样。
  • 但是“C 标准未指定”和“SIGSEGV”是有区别的,明确规定。现在你两个都说。
  • 是的,我确实说明了标准的内容和通常发生的情况。
  • 好的,那么请解释一下为什么宏container_of 不能使用(type *)0)-&gt;member 既不能与SIGSEGV 也不能与not specified by C lang. 错误?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2021-06-09
  • 1970-01-01
  • 2011-05-23
  • 2020-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多