是否需要 C 实现来忽略在评估 void 表达式期间发生的未定义行为,就好像评估本身从未发生过一样?
考虑到 C11,6.3.2.2 §1:
如果任何其他类型的表达式被评估为 void 表达式,则其值或指示符将被丢弃。(评估 void 表达式的副作用。)
这与用于防止编译器警告未使用变量的常用习语有关:
void f() {
int a;
(void)a;
}
但是,如果我们有未定义的行为,例如:
void f() {
int a;
(void)(1/0);
}
我可以安全地声称该程序不包含未定义的行为吗?该标准说“它的值或指示符被丢弃”,但“表达式(...)被评估(...)”,所以评估似乎发生了。
GCC/Clang 确实报告了未定义的行为,因为在这种情况下很明显,但在一个更微妙的例子中它们没有:
int main() {
int a = 1;
int b = 0;
(void)(a/b);
return 0;
}
即使有-O0
,GCC 和 Clang 都不会评估1/0
。但是即使没有演员阵容也会发生这种情况,所以它不具有代表性。
将论点推向极端,(void)a
在我的第一个示例(a
未初始化的地方)中的简单评估不会系统地触发未定义的行为吗?
ISO C11 6.3.2.1 §2 确实提到:
如果左值指定了一个可以使用寄存器存储类声明的具有自动存储持续时间的对象(从未使用过它的地址),并且该对象未初始化(未使用初始化程序声明并且在使用之前未对其进行分配),行为未定义。
但是,在附件 J.2 未定义行为中,措辞略有不同:
在以下情况下,行为未定义:
(...)
一个左值指定一个可以用寄存器存储类声明的自动存储持续时间的对象,在需要指定对象的值的上下文中使用,但该对象未初始化。(6.3.2.1)。
这个附件确实导致了这样一种解释,即void
在其评估期间包含未定义行为的表达式实际上并未被评估,但由于它只是一个附件,我不确定它的论证权重。