【发布时间】:2015-01-10 11:09:35
【问题描述】:
代码示例:
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 (lvalue 的定义)阅读 lvalue that potential指定一个对象 - C99 弄乱了左值的定义,所以 C11 不得不重写它,也许这部分漏掉了。
6.3.2.1/1 确实说:
左值是一个表达式(对象类型不是 void),它可能 指定一个对象;如果左值在计算时未指定对象,则 行为未定义
但是& 运算符确实 评估其操作数。 (它不访问存储的值,但这是不同的)。
这个长长的推理链似乎表明代码导致了 UB,但是它相当脆弱,我不清楚标准的编写者的意图是什么。如果事实上他们有任何意图,而不是让我们来辩论:)
【问题讨论】:
-
当然也可以看看Wikipedia's page on
offsetof。 -
@unwind 似乎没有提供 C 标准没有的任何见解:)
-
第 6.5.2.3.4 段的注释说:"96) 如果
&E是一个有效的指针表达式(其中 & 是 ''address-of'' 运算符,它会生成一个指向其操作数的指针),表达式(&E)->MOS与E.MOS相同。” 我认为这涵盖了.和->之间的关系。 -
@user694733 我没看到;也就是说,如果上述表达式有效,则
(&(((struct name *)NULL)->b))->b与(((struct name *)NULL)->b)->b相同。此注释仅适用于E具有结构类型时,但这里E是int -
@unwind 已创建循环引用,因为 Wikipedia 的页面现在链接到 此处。
标签: c language-lawyer c11 offsetof