繁体   English   中英

C++:捕捉除以零错误

[英]C++ : Catch a divide by zero error

这是一段简单的代码,其中发生了除以零的情况。 我试图抓住它:

#include <iostream>

int main(int argc, char *argv[]) {
    int Dividend = 10;
    int Divisor = 0;

    try {
        std::cout << Dividend / Divisor;
    } catch(...) {
        std::cout << "Error.";
    }
    return 0;
}

但是应用程序无论如何都会崩溃(即使我设置了MinGW 的-fexceptions选项)。

是否有可能捕获这样的异常(我理解这不是 C++ 异常,而是 FPU 异常)?

我知道我可以在除法之前检查除数,但我假设,因为除以零是罕见的(至少在我的应用程序中),尝试除法会更有效(如果它发生)而不是在除法之前每次测试除数。

我正在 WindowsXP 计算机上进行这些测试,但希望使其跨平台。

这也不例外。 这是在硬件级别确定的错误并返回给操作系统,然后操作系统以某种特定于操作系统的方式通知您的程序(例如,通过终止进程)。

相信在这种情况下发生的不是例外而是信号。 如果是这种情况:操作系统会中断程序的主控制流并调用信号处理程序,后者反过来终止程序的运行。

这与您取消引用空指针时出现的错误类型相同(然后您的程序因 SIGSEGV 信号、分段错误而崩溃)。

您可以尝试使用<csignal>标头中的函数来尝试为 SIGFPE 信号提供自定义处理程序(它用于浮点异常,但也有可能是整数除以零引发的情况 - 我真的这里不确定)。 但是,您应该注意信号处理依赖于操作系统,并且 MinGW 以某种方式“模拟”Windows 环境下的 POSIX 信号。


这是在 MinGW 4.5、Windows 7 上的测试:

#include <csignal>
#include <iostream>

using namespace std;

void handler(int a) {
    cout << "Signal " << a << " here!" << endl;
}

int main() {
    signal(SIGFPE, handler);
    int a = 1/0;
}

输出:

信号 8 在这里!

在执行信号处理程序后,系统立即终止进程并显示错误消息。

使用它,您可以在除以零或空指针取消引用后关闭任何资源或记录错误……但与异常不同的是,即使在特殊情况下,这也不是控制程序流程的方法。 一个有效的程序不应该这样做。 捕获这些信号仅用于调试/诊断目的。

(有一些有用的信号通常在低级编程中非常有用,并且不会导致您的程序在处理程序之后立即被终止,但这是一个深入的主题)。

除以零是一个逻辑错误,是程序员的错误。 你不应该试图应付它,你应该调试并消除它。 此外,捕获异常的代价是极其昂贵的——比除数检查要昂贵得多。

您可以使用结构化异常处理来捕获除以零错误。 如何实现取决于您的编译器。 MSVC 提供了一个将结构化异常catch(...)catch(...)的函数,还提供了一个将结构化异常转换为常规异常的__try ,并提供了__try / __except / __finally 但是,我对 MinGW 不够熟悉,无法告诉您如何在该编译器中执行此操作。

  1. 没有一种语言标准的方法可以从 CPU 中捕获除以零。

  2. 不要过早地“优化”掉一个分支。 在这种情况下,您的应用程序真的受CPU 限制吗? 我对此表示怀疑,如果您破坏代码,这并不是真正的优化。 否则,我可以让你的代码更快:

     int main(int argc, char *argv[]) { /* Fastest program ever! */ }

不知何故,仍然缺少真正的解释。

是否有可能捕获这样的异常(我理解这不是 C++ 异常,而是 FPU 异常)?

是的,您的catch块应该适用于某些编译器。 但问题是您的异常不是 FPU 异常 你在做整数除法。 我不知道这是否也是一个可捕获的错误,但它不是 FPU 异常,它使用了浮点数的 IEEE 表示的功能。

在 Windows 上(使用 Visual C++),试试这个:

BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult)
{
    __try 
    { 
        *pResult = dividend / divisor; 
    } 
    __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? 
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    { 
        return FALSE;
    }
    return TRUE;
}

MSDN: http : //msdn.microsoft.com/en-us/library/ms681409( v= vs.85).aspx

为了避免无限的“信号8在这里!” 消息,只需将“退出”添加到 Kos 漂亮的代码中:

#include <csignal>
#include <iostream>
#include <cstdlib> // exit

using namespace std;

void handler(int a) {
    cout << "Signal " << a << " here!" << endl;
    exit(1);
}

int main() {
    signal(SIGFPE, handler);
    int a = 1/0;
}

好吧,如果对此有异常处理,则某些组件实际上需要进行检查。 因此,如果您自己检查,您不会丢失任何东西。 没有什么比简单的比较语句更快的了(一个 CPU 指令“如果等于零则跳转”或类似的东西,不记得名字了)

正如其他人所说,这不是一个例外,它只是生成一个 NaN 或 Inf。

零除法只是一种方法。 如果你做很多数学,有很多方法,比如
log(not_positive_number)exp(big_number)等。

如果您可以在进行计算之前检查有效参数,那么就这样做,但有时这很难做到,因此您可能需要生成和处理异常。

在 MSVC 中有一个头文件#include <float.h>包含一个函数_finite(x) ,它告诉我们一个数字是否是有限的。 我很确定 MinGW 有类似的东西。 您可以在计算后对其进行测试并抛出/捕获您自己的异常或其他任何内容。

暂无
暂无

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

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