C ++: поймать деление на ноль ошибок - PullRequest
27 голосов
/ 20 января 2011

Вот простой фрагмент кода, в котором происходит деление на ноль.Я пытаюсь это уловить:

#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;
}

Но приложение все равно вылетает (хотя я поставил опцию -fexceptions из MinGW ).

ЭтоМожно ли поймать такое исключение (которое, как я понимаю, не исключение C ++, а исключение FPU)?

Я знаю, что могу проверить делитель до деления, но ясделал предположение, что, поскольку деление на ноль встречается редко (по крайней мере, в моем приложении), было бы более эффективно попытаться разделить (и перехватить ошибку, если она возникает), чем проверять каждый раз делитель перед делением.

Я выполняю эти тесты на компьютере с WindowsXP, но хотел бы сделать его кроссплатформенным.

Ответы [ 9 ]

32 голосов
/ 20 января 2011

Это не исключение.Это ошибка , которая определяется на уровне hardware и возвращается обратно в операционную систему, которая затем уведомляет об этом вашу программу определенным для ОС способом (например, убивая процесс).).

Я считаю , что в таком случае происходит не исключение, а сигнал.Если это так: операционная система прерывает основной поток управления вашей программы и вызывает обработчик сигнала, который, в свою очередь, прекращает работу вашей программы.

Это тот же тип ошибки, который появляется при разыменованиинулевой указатель (тогда ваша программа падает по сигналу SIGSEGV, ошибка сегментации).

Вы можете попробовать использовать функции из заголовка <csignal>, чтобы попытаться предоставить собственный обработчик для сигнала SIGFPE (это для исключений с плавающей запятой), но это может быть случай, когда он также увеличивается для целочисленного деления на ноль - я действительно не уверен здесь).Однако следует помнить, что обработка сигналов зависит от ОС, и MinGW каким-то образом «эмулирует» сигналы POSIX в среде Windows.


Вот тест для 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 здесь!

И сразу после выполнения обработчика сигнала система завершает процесс и отображает сообщение об ошибке.

Используя это, вы можете закрыть любые ресурсы или зарегистрировать ошибку после деления на ноль или разыменования нулевого указателя ... но в отличие от исключений, это НЕ способ контролировать поток вашей программы даже в исключительных случаях. Действительная программа не должна этого делать.Захват этих сигналов полезен только для целей отладки / диагностики.

(Есть некоторые полезные сигналы, которые в общем случае очень полезны при низкоуровневом программировании и не приводят к тому, что ваша программа сразу же уничтожается послеобработчик, но это глубокая тема).

13 голосов
/ 20 января 2011

Деление на ноль - логическая ошибка, ошибка программиста.Вы не должны пытаться справиться с этим, вы должны отлаживать и устранять это.Кроме того, перехват исключительных ситуаций чрезвычайно затратен - гораздо больше, чем проверка делителей.

Вы можете использовать Структурированную обработку исключений, чтобы перехватить ошибку деления на ноль.Как это достигается, зависит от вашего компилятора.MSVC предлагает функцию для обнаружения структурированных исключений как catch(...), а также предоставляет функцию для преобразования структурированных исключений в обычные исключения, а также предлагает __try / __except / __finally.Однако я недостаточно знаком с MinGW, чтобы рассказать вам, как это сделать в этом компиляторе.

8 голосов
/ 20 января 2011
  1. Там нет языковой стандарт ловли деление на ноль от процессора.

  2. Не преждевременно «оптимизировать» ветка. Ваша заявка на самом деле В этом контексте ограничен ЦП? Я сомневаюсь в этом, и это не совсем оптимизация, если вы нарушаете свой код. В противном случае, я мог бы сделать ваш код еще быстрее:

    int main(int argc, char *argv[]) { /* Fastest program ever! */ }
    
6 голосов
/ 20 января 2011

Деление на ноль не является исключением в C ++, см. https://web.archive.org/web/20121227152410/http://www.jdl.co.uk/briefings/divByZeroInCpp.html

3 голосов
/ 20 января 2011

Каким-то образом настоящее объяснение все еще отсутствует.

Возможно ли поймать такое исключение (которое, как я понимаю, не исключение C ++, а исключение FPU)?

Да, ваш блок catch должен работать на некоторых компиляторах.Но проблема в том, что ваше исключение не является исключением FPU .Вы делаете целочисленное деление.Я не знаю, является ли это также заразной ошибкой, но это не исключение FPU, которое использует особенность IEEE-представления чисел с плавающей запятой.

1 голос
/ 14 мая 2012

В 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

0 голосов
/ 29 января 2015

Чтобы избежать бесконечности "Сигнал 8 здесь!"сообщения, просто добавьте «выход» в код Kos 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;
}
0 голосов
/ 20 января 2011

Как говорили другие, это не исключение, оно просто генерирует NaN или Inf.

Ноль-деление - это только один из способов сделать это.Если вы много занимаетесь математикой, есть лоты способов, например
log(not_positive_number), exp(big_number) и т. Д.

Если вы можете проверить действительные аргументы перед выполнением вычисления,затем сделайте это, но иногда это трудно сделать, поэтому вам может потребоваться сгенерировать и обработать исключение.

В MSVC есть заголовочный файл #include <float.h>, содержащий функцию _finite(x), которая сообщает, является ли числоконечно.Я почти уверен, что у MinGW есть нечто подобное.Вы можете проверить это после вычисления и бросить / поймать свое собственное исключение или что-то еще.

0 голосов
/ 20 января 2011

Что ж, если произошла обработка исключений по этому поводу, некоторый компонент на самом деле должен был сделать проверку.Поэтому вы ничего не потеряете, если сами это проверите.И это не намного быстрее, чем простая инструкция сравнения (одна единственная инструкция ЦП «прыгай, если равен нулю» или что-то в этом роде, не помню названия)

...