tmy*_*ebu 13 c undefined-behavior language-lawyer
在本文中,以下是一段可以触发除零的代码示例:
if (arg2 == 0)
ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
/* No overflow is possible */
PG_RETURN_INT32((int32) arg1 / arg2);
Run Code Online (Sandbox Code Playgroud)
ereport这是一个宏,它扩展为对 - bool返回函数的调用,该函数errstart可能会或可能不会返回,并且?:对其返回值有条件(使用a ),调用另一个函数.在这种情况下,我认为ereport水平ERROR无条件地导致longjmp()其他地方.
因此,对上述代码的简单解释是,如果arg2非零,则将发生除法并返回结果,而如果arg2为零,则将报告错误并且不会发生除法.然而,链接的论文声称C编译器可以在零检查之前合法地提升除法,然后推断零检查从未被触发.他们唯一的推理似乎是不正确的
程序员没有通知编译器对ereport的调用(ERROR,:: :)没有返回.这意味着除法将始终执行.
John Regehr有一个更简单的例子:
void bar (void);
int a;
void foo3 (unsigned y, unsigned z)
{
bar();
a = y%z;
}
Run Code Online (Sandbox Code Playgroud)
根据这篇博文,clang在调用之上提升了模运算bar,并且他展示了一些汇编代码来证明它.
我对C的理解是适用于这些片段的
没有或不可以返回的函数在标准C中是格式良好的,并且这样的声明不需要特定的属性,钟声或哨声.
调用不返回或不返回的函数的语义是明确定义的,特别是通过C99中的6.5.2.2"函数调用".
由于ereport调用是一个完整的表达式,因此有一个序列点;.类似地,由于barJohn Regehr的代码中的调用是一个完整的表达式,因此在该处有一个序列点;.
因此,在ereport调用或bar调用与除法或模数之间存在一个序列点.
C编译器可能不会向未自行引发未定义行为的程序引入未定义的行为.
这五点似乎足以得出结论,上面的除零测试是正确编写的,并且在调用之上提升模数bar是不正确的.两个编译器和许多专家不同意.我的推理出了什么问题?
这篇文章是错误的,至于clang示例,这是一个编译器错误(clang的一个相当常见的例子......).我希望我能给你更好的理由,但你已经在问题中提供了所有正确的推理.
实际上,对于clang问题,据我所知,还没有证明任何错误.由于在您链接到的博客上的示例bar 中返回,编译器可以自由地对整个调用中的分区重新排序.如果bar在同一个翻译单元中定义,这是微不足道的,但LTO也可以.要真正测试此错误,您需要一个bar永不返回的函数.
| 归档时间: |
|
| 查看次数: |
517 次 |
| 最近记录: |