具有此功能:
#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) );})
编译器未给出任何具体错误,但导致程序崩溃。为什么也不使宏崩溃?
答案 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)