我不太明白为什么我没有被零例外除:
int d = 0;
d /= d;
我希望得到除以零的异常,但会改为d == 1
。
为什么d /= d
时d == 0
不会被零除?
答案 0 :(得分:106)
C ++没有要捕获的“零除”异常。您观察到的行为是编译器优化的结果:
d == 0
)中未定义行为的条件一定不会发生d / d
必须始终等于1。我们可以强制编译器以零次调整代码来触发除以零的“实数”除法。
volatile int d = 0;
d /= d; //What happens?
所以现在问题仍然存在:既然我们基本上已经迫使编译器允许这种情况发生,那会发生什么?这是未定义的行为-但是我们现在阻止了编译器针对这种未定义的行为进行优化。
通常,这取决于目标环境。这不会触发软件异常,但是它可以(取决于目标CPU)触发硬件异常(“整数除以零”),无法以传统方式捕获软件可以捕获异常。对于x86 CPU和大多数其他(但不是全部!)架构,绝对是这种情况。
但是,有一些方法可以处理硬件异常(如果发生),而不仅仅是让程序崩溃:请看一下这篇文章,了解一些可能适用的方法:Catching exception: divide by zero。请注意,它们因编译器而异。
答案 1 :(得分:37)
仅是对其他答案的补充,事实是除以零是未定义的行为,这意味着编译器在可能发生的情况下可以自由执行任何操作:
0 / 0 == 1
并进行相应的优化。这实际上是它在这里所做的。0 / 0 == 42
并将d
设置为该值。d
的值是不确定的,从而使该变量未初始化,因此其值将等于先前写入为其分配的内存中的任何值。注释中在其他编译器上观察到的一些意外值可能是由这些编译器执行此类操作引起的。d
的原始值,编译器仍可以假定它永远不会为零,并相应地优化代码。在特定的OP代码情况下,仅假设0 / 0 == 1
与编译器实际上是无法区分的,但是例如,编译器还可以假设puts()
中的if (d == 0) puts("About to divide by zero!"); d /= d;
永远不会被执行! 答案 2 :(得分:28)
C ++标准未定义整数被零除的行为。不需要引发异常。
(浮点除以零也未定义,但IEEE754对其进行了定义。)
您的编译器正在将d /= d
优化为有效的d = 1
,这是一个合理的选择。可以进行此优化,因为可以假定代码中没有未定义的行为-d
不可能为零。
答案 3 :(得分:1)
了解发生了什么的最简单方法是查看程序集输出
int divide(int num) {
return num/num;
}
将为 x86-64 生成
divide(int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov eax, 1
pop rbp
ret
正如你所看到的,这里没有除法运算,
但我们有mov eax, 1
。
这里是复制链接:https://godbolt.org/z/MbY6Wqh4T
答案 4 :(得分:0)
请注意,在这种情况下(以及其他情况下),您可以使用boost安全数字来使代码生成C ++异常。 https://github.com/boostorg/safe_numerics