我不太明白为什么我没有被零例外除以:
int d = 0;
d /= d;
我本来希望得到除以零的除法运算,但是反而d == 1
。
为什么在什么时候不d /= d
将被零除d == 0
?
我不太明白为什么我没有被零例外除以:
int d = 0;
d /= d;
我本来希望得到除以零的除法运算,但是反而d == 1
。
为什么在什么时候不d /= d
将被零除d == 0
?
throw
语句引发异常。没有别的(除非您处在行为不确定的领域)。
Answers:
C ++没有要捕获的“零除”异常。您观察到的行为是编译器优化的结果:
d == 0
)中未定义行为的条件一定不会发生d / d
必须始终等于1。我们可以强制编译器以零次调整代码来触发除以零的“实际”除法。
volatile int d = 0;
d /= d; //What happens?
所以现在问题仍然存在:既然我们基本上已经迫使编译器允许这种情况发生,那会发生什么?这是未定义的行为-但是我们现在阻止了编译器针对这种未定义的行为进行优化。
通常,它取决于目标环境。这不会触发软件异常,但是可以(取决于目标CPU)触发硬件异常(零整数分割),无法以传统方式捕获软件异常。对于x86 CPU和大多数其他(但不是全部!)架构,绝对是这种情况。
但是,有一些处理硬件异常(如果发生)的方法,而不仅仅是让程序崩溃:在这篇文章中寻找一些可能适用的方法:捕获异常:除以零。请注意,它们因编译器而异。
1
是完全有效的任何东西。获得14684554的原因必须是编译器进一步优化-它传播初始d==0
条件,因此不仅可以得出“这是1或UB”的结论,而且实际上可以得出“这是UB,周期”的结论。因此,生成加载该常量的代码甚至不费吹灰之力1
。
只是为了补充其他答案,以零除是未定义的行为这一事实意味着编译器在发生这种情况的情况下可以自由执行任何操作:
0 / 0 == 1
并相应地进行优化。这实际上是这里所做的。0 / 0 == 42
并设置d
为该值。d
不确定,从而使变量未初始化,以便其值可以是先前写入分配给它的内存中的任何值。注释中在其他编译器上观察到的一些意外值可能是由那些编译器执行此类操作引起的。d
在编译时不知道的原始值,编译器仍可以假定它永远不会为零,并相应地优化代码。在OP代码的特定情况下,仅假设,这实际上与编译器是没有区别的0 / 0 == 1
,但是例如,编译器还可以假设puts()
inif (d == 0) puts("About to divide by zero!"); d /= d;
永远不会执行!C ++标准未定义零除整数的行为。这是不需要抛出异常。
(浮点除以零也未定义,但IEEE754对其进行了定义。)
您的编译器正在d /= d
有效地d = 1
进行优化,这是一个合理的选择。可以进行此优化,因为可以假设代码中没有未定义的行为-d
不可能为零。
d
不可能是零,”你还以为编译器不看行:int d = 0;
?? :)
请注意,在这种情况下(和其他情况下),您可以使用boost安全数字来使代码生成C ++异常。https://github.com/boostorg/safe_numerics