Catching exception: divide by zero

后端 未结 8 1751
时光取名叫无心
时光取名叫无心 2020-11-22 16:23

The following code does not catch an exception, when I try to divide by 0. Do I need to throw an exception, or does the computer automatically throw one at runtime?

8条回答
  •  情书的邮戳
    2020-11-22 17:00

    setjmp + longjmp

    https://stackoverflow.com/a/25601100/895245 mentioned the possibility or throwing a C++ exception from a signal handler, but Throwing an exception from within a signal handler mentions several caveats of that, so I would be very careful.

    As another potentially dangerous possibility, you can also try to use the older C setjmp + longjmp mechanism as shown at: C handle signal SIGFPE and continue execution

    main.cpp

    #include 
    #include 
    #include 
    #include 
    
    jmp_buf fpe;
    
    void handler(int signum) {
        longjmp(fpe, 1);
    }
    
    int main() {
        volatile int i, j;
        for(i = 0; i < 10; i++) {
            struct sigaction act;
            struct sigaction oldact;
            memset(&act, 0, sizeof(act));
            act.sa_handler = handler;
            act.sa_flags = SA_NODEFER | SA_NOMASK;
            sigaction(SIGFPE, &act, &oldact);
            if (0 == setjmp(fpe)) {
                std::cout << "before divide" << std::endl;
                j = i / 0;
                sigaction(SIGFPE, &oldact, &act);
            } else {
                std::cout << "after longjmp" << std::endl;
                sigaction(SIGFPE, &oldact, &act);
            }
        }
        return 0;
    }
    

    Compile and run:

    g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
    ./main.out
    

    Output:

    i = 0
    before divide
    after longjmp
    i = 1
    before divide
    after longjmp
    i = 2
    before divide
    after longjmp
    

    man longjmp says that you can longjmp from signal handlers, but with a few caveats:

    POSIX.1-2008 Technical Corrigendum 2 adds longjmp() and siglongjmp() to the list of async-signal-safe functions. However, the standard recommends avoiding the use of these functions from signal handlers and goes on to point out that if these functions are called from a signal handler that interrupted a call to a non-async-signal-safe function (or some equivalent, such as the steps equivalent to exit(3) that occur upon a return from the initial call to main()), the behavior is undefined if the program subsequently makes a call to a non-async-signal-safe function. The only way of avoiding undefined behavior is to ensure one of the following:

    • After long jumping from the signal handler, the program does not call any non-async-signal-safe functions and does not return from the initial call to main().

    • Any signal whose handler performs a long jump must be blocked during every call to a non-async-signal-safe function and no non-async-signal-safe functions are called after returning from the initial call to main().

    See also: Longjmp out of signal handler?

    However Throwing an exception from within a signal handler mentions that this has further dangers with C++:

    setjmp and longjmp aren't compatible with exceptions and RAII (ctors/dtors), though. :( You'll probably get resource leaks with this.

    so you would have to be very very careful with that as well.

    I guess the moral is that signal handlers are hard, and you should avoid them as much as possible unless you know exactly what you are doing.

    Detect floating point zero division

    It is also possible to detect floating point division by zero with a glibc call to:

    #include 
    
    feenableexcept(FE_INVALID);
    

    as shown at: What is the difference between quiet NaN and signaling NaN?

    This makes it raises SIGFPE as well like the integer division by zero instead of just silently qnan and setting flags.

提交回复
热议问题