visualis除以零并伴有运行时错误,这在c++中非常有用

visual Is dividing by zero accompanied with a runtime error ever useful in C++?

本文关键字:这在 c++ 有用 非常 运行时错误 visualis      更新时间:2023-10-16

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

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

Visual c++编译if -语句如下:

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

显然编译器看到代码是要除以0 -它使用xor x,x模式将ecx归零,然后作为整数除法的第二个操作数。这段代码肯定会在运行时触发"整数除零"错误。

在我看来,这种情况(当编译器知道代码总是被0除时)值得一个编译时错误——标准并没有禁止这种情况。这将有助于在编译时而不是在运行时诊断此类情况。

然而,我和其他几个开发人员谈过,他们似乎不同意——他们的反对意见是"如果作者想除以0得到……嗯…测试错误处理?"

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

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

更具可读性和可维护性。当"需要测试错误处理"时,可以使用该函数,并且在所有其他情况下都有一个不错的编译时错误。

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

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

而且,我在实际代码中看到的大多数除零错误都是与输入相关的,或者至少不适合静态分析。也许不值得进行检查。

除以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的行为的人…会非常失望的。

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

然而,对于符合ieee标准的硬件上的浮点类型,显式除以零是非常有用的。你可以用它来产生正极&负无穷(+/- 1/0),而不是一个数字(NaN, 0/0)值,这可能非常有用。

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

出于数据分析的目的,您可以使用nan来指示丢失或无效的数据,然后可以优雅地处理这些数据。例如,Matlab使用显式NaN值来抑制图中缺失的数据等。

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

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

在编译时检测除零是你想要的编译器警告。这真是个好主意。

我和microsoftvisualc++没有任何关系,但是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’

但是考虑到错误是一个滑坡,懂行的人知道不要花太多的业余时间去爬它。考虑一下为什么当我写

时,c++没有什么可说的:
int main() {
    while (true) {
    }
    return 0;
}

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