从数学上讲,0 / 0是未定义的。但是,在C编程中(特别是gcc编译器gcc-7.3.0在ubuntu上),它以1的形式给出了答案。我想知道原因。它是通过重复减法工作吗?对于相同的代码,如果我使用n = 1 / n,我会得到一个浮点异常。然而,对于gcc-5.3.0,它会产生一个错误。
#include <stdio.h>
int main() {
int n = 5;
n = n - 5;
switch (n) {
case 0:
printf("n= %d", n);
n = n / n;
printf("n calc= %d", n);
break;
case 5:
printf("n=5");
break;
default:
printf("n=1");
break;
}
return 0;
}发布于 2019-04-30 09:01:33
除以零是C编程中未定义的行为(C11 6.5.5§6)。这意味着没有可预测的结果,也不知道你的程序会做什么。
试着解释为什么你会得到“任何事情都可能发生”的特定结果,这并不是很有意义的。它可能打印1,它可能打印42,它可能什么都不打印。程序可能会崩溃和烧毁。如果您运行该程序两次,您可能会得到不同的结果。诸若此类。
至于为什么不能获得编译器错误,编译器不能或必须查找或诊断运行时错误。程序员需要避免出现大多数未定义行为的情况。
如果使用纯整数常量表达式(如int x = 0/0; ),一些优秀的编译器可能会发出警告,但编译器也不需要找到这个bug。这是程序员的工作。因此,在应用操作数之前,您应该养成始终检查/或%的正确操作数是否为零的习惯。
发布于 2019-04-30 09:18:43
编译器可以假设代码不调用未定义的行为,并在优化代码时使用该假设。
对于除n==0以外的任何值,表达式n/n计算为1。因为在C中,零跳转是未定义的行为,所以编译器可以假设这种情况永远不会发生,也就是说,它可以假设n!=0。因为它可能假定,编译器可以用廉价的常量值1替换慢的1。这可能就是所讨论的代码发生的情况。
当然,编译器不需要做任何这样的假设,可能会生成一个实际的除法。根据执行代码的硬件,在这种除法为零的情况下,可能会触发特定的硬件信号(或异常)。在本例中,对于优化的代码,我怀疑任何合理的编译器都会发出除法,因为除法非常慢,而且按照标准,常量1足够好。但是,它仍然有可能在调试模式下产生实际的分割。
在优化其他未定义的行为(如int溢出)时,也会发生类似的情况:
void f(int x)
{
if (x > x + 1)
// executed only in case of overflow,
// which is undefined behavior,
// and hence likely optimized out.
overflow_error();
...不要依赖未定义的行为--这是不可预测的。
发布于 2019-04-30 12:19:57
在戈德波特的编译器探险家上可以看到,gcc和clang都对n整数的n / n进行优化,以便在没有任何警告的情况下计算为1。gcc在-O0执行这种优化甚至,而clang为-O0生成除法操作码,但为-O1和更高级别生成优化的备选方案。
0 / 0无论如何都有未定义的行为,因此n / n的任何行为都是可以的,如果n为零,包括对1的评估,没有明显的副作用。
如果坚持,您可以将n限定为volatile,编译器可能会生成一个除法,就像在本例中gcc和clang所做的那样,但是只要n被读取两次,它就不必这样做。
https://stackoverflow.com/questions/55916832
复制相似问题