繁体   English   中英

C中的0/0-gcc-7或更高

[英]0/0 in C - gcc-7 or higher

在数学上, 0 / 0是不确定的。 但是,在C编程中(尤其是ubuntu上的gcc编译器gcc-7.3.0),其答案为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;
}

除以零是C编程中未定义的行为(C11 6.5.5§6)。 意味着没有可预测的结果,也没有告诉您程序可能会做什么。

尝试为什么会得到“可能发生的任何事情”的特定结果并不是很有意义。 它可能会打印1,可能会打印42,也可能什么也不打印。 该程序可能会崩溃并刻录。 如果您两次运行该程序,则可能会得到不同的结果。 等等。

至于为什么不会出现编译器错误,则编译器无法或没有义务发现或诊断运行时错误。 程序员需要避免大多数未定义行为的情况。

如果您使用纯整数常量表达式,例如int x = 0/0;一些好的编译器可能会发出警告int x = 0/0; ,但再次发现此错误不是编译器的工作。 这是程序员的工作。 因此,您应该养成在应用操作数之前始终检查/%的正确操作数是否为零的习惯。

允许编译器假设代码没有调用未定义的行为,并在优化代码时使用该假设。

对于除n==0以外的任何值,表达式n/n计算结果均为1。由于在C中,零除是未定义的行为,因此编译器可以假定这种情况永远不会发生,即可以假定n!=0 由于可以假设,编译器可以用便宜的常数1代替慢n/n 这可能是有关代码发生的情况。

当然,编译器不需要做任何这样的假设,并且可能会产生实际的除法。 根据执行代码的硬件,在被零除的情况下,可能会触发特定的硬件信号(或异常)。 我怀疑在此示例中任何合理的编译器都将为优化代码发出除法,因为除法速度非常慢,并且根据标准,常数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();
   ...

不要依赖未定义的行为-这是不可预测的。

Godbolt的编译器浏览器中可以看出, gccclang都将n / n n整数的n / n优化为1而没有任何警告。 gcc 即使-O0 执行此优化,而clang-O0生成除法操作码,但为-O1及更高版本生成优化的替代方法。

0 / 0仍然具有未定义的行为,因此,如果n为零,则对n / n任何行为都是可以的,包括评估为1而没有明显的副作用。

如果您坚持认为,可以将nvolatile ,并且编译器可能会生成除法,就像在这种情况下gccclang一样,但是只要将n读取两次,就不必这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM