在没有警告的情况下取消初始化结构
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)
有这个:
#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)