有这个。
#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)