为什么此C代码段中的NULL取消引用不会导致未定义的行为

为什么此C代码段中的NULL取消引用不会导致未定义的行为,c,pointers,gcc,struct,c-preprocessor,C,Pointers,Gcc,Struct,C Preprocessor,我遇到了一段代码,其中NULL类型转换为结构指针类型foo*0,该指针反引用一个成员foo*0->m,使用该成员的地址&foo*0->m并将其类型转换为整数,以获得该成员在结构中的内存索引。unsigned int&foo*0->m 据我所知,在C语言中,空指针取消引用总是会导致分段错误。但我不明白空指针如何可以像这样取消引用,并且仍然不会导致分段错误 #include <stdio.h> #define MACRO(m) ((unsigned int)(&(((foo *

我遇到了一段代码,其中NULL类型转换为结构指针类型foo*0,该指针反引用一个成员foo*0->m,使用该成员的地址&foo*0->m并将其类型转换为整数,以获得该成员在结构中的内存索引。unsigned int&foo*0->m

据我所知,在C语言中,空指针取消引用总是会导致分段错误。但我不明白空指针如何可以像这样取消引用,并且仍然不会导致分段错误

#include <stdio.h>
#define  MACRO(m) ((unsigned int)(&(((foo *)0)->m)))

typedef struct
{
int a;
int b;
int c;
int d;
}foo;

int main(void) {
    printf("\n  %d  ", MACRO(c));
    return 0;
}
C11标准在6.5.3.2地址和间接运算符中规定:

一元&运算符生成其操作数的地址。如果操作数具有类型,则结果具有指向类型的类型指针。如果操作数是一元*运算符的结果,则不会对该运算符和&运算符求值,并且结果就像两者都被省略一样,但运算符上的约束仍然适用,且结果不是左值。类似地,如果操作数是[]运算符的结果,则不会计算[]所隐含的&运算符或一元数*,结果就像移除了&运算符,并将[]运算符更改为+运算符一样。否则,结果是指向由其操作数指定的对象或函数的指针

我的。脚注说:

因此,即使E是空指针,&*E也相当于E

有趣的是,->操作符也不例外。我不知道这是故意的还是疏忽。因此,在对标准的严格解释中,我认为&foo*0->m是未定义的行为。但这并不意味着程序必须崩溃或编译器必须抱怨


这就是说,在获取->运算符结果的地址时,完全可以做出相同的异常,这也是大多数编译器所做的。

此代码段生成输出8。取消引用空指针是未定义的行为,未定义的行为是未定义的。这基本上是人们手工实现的方式。它使用未定义的行为,但大多数编译器只是接受了它。@Adi从未读取该值,只读取指向该值的指针。我不认为这是重复的。另一个问题是关于未定义行为的含义。这是关于一个特定的案例,以及编译器如何处理代码。说未定义的行为是未定义的并不能解决本海报中的误解,重复的.p->m也不能等同于*p.m,但在标准中提到的特殊例外情况中,&-运算符的操作数是*运算符的结果,而在&*p.m中,&的操作数是。-运算符的结果。因此p->m是p==NULL的未定义行为。