fre*_*low 43 c c++ compile-time-constant divide-by-zero constant-expression
如果我在常量表达式中除以零,我的玩具编译器会崩溃:
int x = 1 / 0;
Run Code Online (Sandbox Code Playgroud)
C和/或C++标准是否允许这种行为?
Sha*_*our 40
是的,除以零是未定义的行为,在这种情况下C和C++标准都没有强加任何要求.虽然在这种情况下我认为你至少应该发布一个诊断(见下文).
在我引用标准之前,我应该注意,尽管这可能是符合要求的行为,但实施的质量是一个不同的问题,仅仅是符合要求并不是有用的.据我所知,gcc,clang,Visual Studio和Intel(根据tpg2114)团队认为内部编译器错误(ICE)是应该报告的错误.应该注意的是,无论提供的标志如何,当前的gcc和clang都会对这种情况发出警告.在两个操作数都是文字/常量的情况下,我们在这里的情况,似乎很容易检测并为此提供诊断.clang为此案例生成以下诊断(请参见实时):
warning: division by zero is undefined [-Wdivision-by-zero]
int x = 1 / 0 ;
^ ~
Run Code Online (Sandbox Code Playgroud)
从草案C11标准部分6.5.5乘法运算符(强调我的):
/运算符的结果是第一个操作数除以第二个操作数的商; [...] 如果第二个操作数的值为零,则行为未定义.
所以它是未定义的行为.
草案C++标准部分5.6 [expr.mul]说:
二元/运算符产生商[...] 如果/或%的第二个操作数为零,则行为未定义 [...]
再次未定义的行为.
C++标准草案和草案C标准对未定义行为都有类似的定义,他们都说:
[......]本国际标准没有规定任何要求
这句话没有任何要求似乎也允许任何行为,包括鼻子恶魔.两者都有类似的说明,说的是:
当本国际标准忽略任何明确的行为定义或程序使用错误的构造或错误数据时,可能会出现未定义的行为.允许不确定的行为从与不可预测的结果完全无视的情况的范围内,在环境中的一个记录的方式特性翻译或程序执行期间行为(具有或不发出一个诊断消息的),以终止翻译或执行(与发行一条诊断信息).
因此,虽然注释不是规范性的,但似乎如果您要在翻译期间终止,您至少应该发布诊断.术语终止没有定义,因此很难说它允许什么.我不认为我曾经看过clang和gcc有没有诊断的ICE的情况.
代码是否必须执行?
如果我们读取将永远不会执行的Can代码调用未定义的行为?我们可以看到,至少在C的情况下,有1 / 0必要执行以便调用未定义的行为的辩论空间.更糟糕的是,在C++情况下,行为定义不存在,因此用于C案例的部分分析不能用于C++案例.
似乎如果编译器可以证明代码永远不会被执行那么我们可以推断它将是- 如果程序没有未定义的行为,但我不认为这是可证明的,只是合理的行为.
从C角度来看,WG14缺陷报告109进一步阐明了这一点.给出以下代码示例:
int foo()
{
int i;
i = (p1 > p2); /* Must this be "successfully translated"? */
1/0; /* Must this be "successfully translated"? */
return 0;
}
Run Code Online (Sandbox Code Playgroud)
并且回应包括:
此外,如果给定程序的每个可能的执行都会导致未定义的行为,则给定的程序不严格符合.
一致的实现必须简单地转换严格符合的程序,因为该程序的某些可能的执行将导致未定义的行为.因为foo可能永远不会被调用,所以给定的示例必须通过一致的实现成功地转换.
因此,在C的情况下,除非可以保证将执行调用未定义行为的代码,否则编译器必须成功转换程序.
C++ constexpr案例
如果x是constexpr变量:
constexpr int x = 1 / 0 ;
Run Code Online (Sandbox Code Playgroud)
这将是不正确的,gcc会产生警告,并且铿锵声会使其出错(请参见实时):
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = 1/ 0 ;
^ ~~~~
note: division by zero
constexpr int x = 1/ 0 ;
^
warning: division by zero is undefined [-Wdivision-by-zero]
constexpr int x = 1/ 0 ;
^ ~
Run Code Online (Sandbox Code Playgroud)
有用地注意到除零是不确定的.
草案C++标准部分5.19常量表达式[expr.const]说:
条件表达式e是核心常量表达式,除非根据抽象机器(1.9)的规则评估e将评估以下表达式之一
并包括以下项目:
具有未定义行为的操作[注意:包括,例如,有符号整数溢出(第5章),某些指针算术(5.7),除零(5.6)或某些移位操作(5.8) - 末尾注释];
在C11中,1/0是一个常量表达式
1 / 0在C11中不是常量表达式,我们可以从6.6常量表达式中看到这一点,它表示:
每个常量表达式应计算为其类型的可表示值范围内的常量.
虽然,它确实允许:
实现可以接受其他形式的常量表达式.
因此1 / 0,在C或C++中不是常量表达式,但这不会改变答案,因为它不在需要常量表达式的上下文中使用.我怀疑OP意味着1 / 0可用于常量折叠,因为两个操作数都是文字,这也可以解释崩溃.
use*_*ica 24
仅存在1 / 0不允许编译器崩溃.最多允许假设表达式永远不会被计算,因此,执行永远不会到达给定的行.
如果保证表达式被计算,则标准对程序或编译器没有要求.然后编译器可能会崩溃.
在C11标准给出一个明确的例子中1 / 0所定义的行为未评估的时候:
因此,在以下初始化中,
Run Code Online (Sandbox Code Playgroud)static int i = 2 || 1 / 0;表达式是一个有效的整数常量表达式,其值为1.
第6.6节,脚注118.
在Constraints下,C11标准的第6.6节说
- 常量表达式不应包含赋值,递增,递减,函数调用或逗号运算符,除非它们包含在未评估的子表达式中.
- 每个常量表达式应计算为其类型的可表示值范围内的常量.
由于1/0不会在int表示的值范围内求值为常量,因此1/0不是常量表达式.这是关于什么算作常量表达式的规则,例如关于不在其中进行赋值的规则.你可以看到,至少对于C++,Clang并不认为1/0是一个常量表达式:
prog.cc:3:18: error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = 1/ 0 ;
^ ~~~~
Run Code Online (Sandbox Code Playgroud)
(x == 0) ? x : 1 / x即使x为0并且评估1/x是UB,也是非常明确的.如果(0 == 0) ? 0 : 1 / 0是UB 的情况,那将是无稽之谈.
use*_*733 15
从C标准草案(N1570):
6.5.5乘法运算符
...
- /运算符的结果是第一个操作数除以第二个操作数的商; %运算符的结果是余数.在这两个操作中,如果第二个操作数的值为零,则行为未定义.
关于第3章中未定义的行为.术语,定义和符号:
3.4.3
- 未
使用的行为行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准不对其施加任何要求- 注意可能的不确定的行为从与不可预测的结果完全无视的情况,范围以翻译期间行为或程序执行环境中的一个记录的方式的特性(有或没有发出一个诊断消息的),以终止翻译或执行(与发布诊断消息).
因此允许崩溃编译器.
| 归档时间: |
|
| 查看次数: |
4102 次 |
| 最近记录: |