3

根据 C++ 标准 (5/5) 除以零是未定义的行为。现在考虑这段代码(有很多无用的语句来防止编译器优化代码):

int main()
{
    char buffer[1] = {};
    int len = strlen( buffer );
    if( len / 0 ) {
        rand();
    }
}

Visual C++if像这样编译 -statement:

sub         eax,edx 
cdq 
xor         ecx,ecx 
idiv        eax,ecx 
test        eax,eax 
je          wmain+2Ah (40102Ah) 
call        rand

很明显,编译器看到代码要除以零——它使用xor x,x模式将其清零ecx,然后在整数除法中为第二个操作数提供服务。此代码肯定会在运行时触发“整数除以零”错误。

IMO 这种情况(当编译器知道代码将始终除以零时)值得编译时错误 - 标准并不禁止这种情况。这将有助于在编译时而不是在运行时诊断此类情况。

然而,我与其他几位开发人员交谈,他们似乎不同意——他们的反对意见是“如果作者想除以零来... emm... 测试错误处理怎么办?”

在没有编译器意识的情况下故意除以零并不难 - 使用__declspec(noinline)Visual C++ 特定的函数装饰器:

__declspec(noinline)
void divide( int what, int byWhat )
{
    if( what/byWhat ) {
       rand();
    }
}

void divideByZero()
{
    divide( 0, 0 );
}

这更具可读性和可维护性。当他“需要测试错误处理”并且在所有其他情况下都有很好的编译时错误时,可以使用该功能。

我错过了什么吗?是否有必要允许编译器知道除以零的代码的发射?

4

5 回答 5

4

除以 0 是未定义的行为,因为它可能会在某些平台上触发硬件异常。我们都希望有一个性能更好的硬件,但是由于没有人认为整数具有 -INF/+INF 和 NaN 值是合适的,所以它是毫无意义的。

现在,因为它是未定义的行为,可能会发生有趣的事情。我鼓励您阅读 Chris Lattner 关于未定义行为和优化的文章,我将在这里简单举个例子:

int foo(char* buf, int i) {
  if (5 / i == 3) {
    return 1;
  }

  if (buf != buf + i) {
    return 2;
  }

  return 0;
}

因为i被用作除数,所以它不是0。因此,第二个if是平凡的,可以优化掉。

面对这样的转变,任何希望除以 0...的理智行为的人都会大失所望。

于 2011-10-17T07:13:38.477 回答
4

可能有一些代码在从未调用过的函数中意外被零除(例如,由于某些特定于平台的宏扩展),这些代码将不再与您的编译器一起编译,从而使您的编译器变得不那么有用。

此外,我在实际代码中看到的大多数除以零错误都是依赖于输入的,或者至少不适合静态分析。也许不值得进行检查。

于 2011-10-17T07:05:31.977 回答
1

在整数类型(int, short, long等)的情况下,我想不出有意除以零的任何用途。

但是,对于 IEEE 兼容硬件上的浮点类型,显式除以零非常有用。您可以使用它来产生正负无穷大 (+/- 1/0),而不是数字 (NaN, 0/0) 值,这非常有帮助。

在排序算法的情况下,您可以使用无穷大作为初始值,表示大于或小于所有可能值。

出于数据分析的目的,您可以使用 NaN 来指示丢失或无效的数据,然后可以对其进行妥善处理。例如,Matlab 使用显式 NaN 值来抑制绘图中的缺失数据等。

虽然您可以通过宏和 std::numeric_limits(在 C++ 中)访问这些值,但能够自己创建它们很有用(并且可以避免大量“特殊情况”代码)。它还允许标准库的实现者避免求助于骇客(例如手动组装正确的 FP 位序列)来提供这些值。

于 2011-10-17T07:35:41.993 回答
0

如果编译器检测到除以 0,则编译器错误绝对没有错。您与之交谈的开发人员是错误的-您可以将该逻辑应用于每个编译错误。除以 0 是没有意义的。

于 2011-10-17T06:58:51.820 回答
0

在编译时检测除以零是您希望成为编译器警告的事情。这绝对是个好主意。

我不与 Microsoft Visual C++ 合作,但 G++ 4.2.1确实进行了此类检查。尝试编译:

#include <iostream>

int main() {
    int x = 1;
    int y = x / 0;
    std::cout << y;
    return 0;
}

它会告诉你:

test.cpp: In function ‘int main()’:
test.cpp:5: warning: division by zero in ‘x / 0’

但考虑到错误是一个滑坡,精明的人知道不要花费太多的业余时间攀登。想想为什么我写的时候 G++ 没有什么可说的:

int main() {
    while (true) {
    }
    return 0;
}

你认为它应该编译它,还是给出一个错误?它应该总是发出警告吗?如果您认为它必须干预所有此类情况,我急切地等待您编写的编译器副本,它只编译保证成功终止的程序!:-)

于 2011-10-17T07:46:23.573 回答