代码示例:
struct name
{
int a, b;
};
int main()
{
&(((struct name *)NULL)->b);
}
这会导致未定义的行为吗?我们可以辩论它是否“取消引用 null”,但是 C11 没有定义术语“取消引用”。
6.5.3.2/4 明确表示*
在空指针上使用会导致未定义的行为;但是它并没有说同样的->
意思,也没有定义a -> b
为存在(*a).b
;它对每个运算符都有单独的定义。
->
6.5.2.3/4 中的语义说:
后缀表达式后跟 -> 运算符和标识符指定结构或联合对象的成员。该值是第一个表达式指向的对象的命名成员的值,并且是一个左值。
但是,NULL
不指向一个对象,所以第二句话似乎没有指定。
同样相关的可能是 6.5.3.2/1:
约束:
一元运算
&
符的操作数应该是一个函数指示符、一个[]
或一元运算符的结果*
,或者是一个左值,它指定一个不是位域且未使用寄存器存储类说明符声明的对象。
但是我觉得粗体文本是有缺陷的,应该读取可能指定对象的左值,根据 6.3.2.1/1(左值的定义) ——C99 弄乱了左值的定义,所以 C11 不得不重写它,也许这个部分错过了。
6.3.2.1/1 确实说:
左值是一个表达式(对象类型不是 void),它可能指定一个对象;如果左值在评估时未指定对象,则行为未定义
但是&
运算符确实评估其操作数。(它不访问存储的值,但这是不同的)。
这条长长的推理链似乎表明代码导致了 UB,但是它相当脆弱,而且我不清楚标准的编写者的意图是什么。如果事实上他们有任何意图,而不是让我们来辩论:)