在没有警告的情况下取消初始化结构

uninitialize struct without warning

有这个:

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

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

你真正的问题是关于

之间的区别
typeof( ((struct Foo*)0)->a )    // Relevant code from the macro.

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

让我们从使用有效指针 p 而不是 0 开始,然后问问自己以下两个片段的作用:

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)->a ) 的标准是什么?

我不知道。


int i = ((struct Foo*)0)->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)