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

时间:2020-05-06 20:00:15

标签: c pointers language-lawyer

具有此功能:

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

编译器未给出任何具体错误,但导致程序崩溃。为什么也不使宏崩溃?

1 个答案:

答案 0 :(得分:0)

您真正的问题实际上是关于两者之间的区别

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)