Ловля исключения: делить на ноль - PullRequest
32 голосов
/ 25 мая 2011

Следующий код не перехватывает исключение, когда я пытаюсь разделить на 0. Нужно ли генерировать исключение или компьютер автоматически выдает его во время выполнения?

int i = 0;

cin >> i;  // what if someone enters zero?

try {
    i = 5/i;
}
catch (std::logic_error e) {

    cerr << e.what();
}

Ответы [ 8 ]

54 голосов
/ 25 мая 2011

Вам нужно проверить это самостоятельно и выдать исключение.Целочисленное деление на ноль не является исключением в стандарте C ++.

Также нет деления с плавающей запятой на ноль, но, по крайней мере, для этого есть специальные средства.

Исключения, перечисленные в стандарте ISOявляются:

namespace std {
    class logic_error;
        class domain_error;
        class invalid_argument;
        class length_error;
        class out_of_range;
    class runtime_error;
        class range_error;
        class overflow_error;
        class underflow_error;
}

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

Однако в разделе 5.6 (из C++11, хотя я не думаю, что это изменилось по сравнению с предыдущей итерацией) конкретно говорится:

Если второй операнд / или % равен нулю, поведение не определено.

Итак, он может бросить эти(или любые другие) исключения.Он также может отформатировать ваш жесткий диск и насмешливо рассмеяться: -)


Если вы хотите реализовать такого зверя, вы можете использовать что-то вроде intDivEx в следующей программе (используя вариант переполнения):

#include <iostream>
#include <stdexcept>

// Integer division, catching divide by zero.

inline int intDivEx (int numerator, int denominator) {
    if (denominator == 0)
        throw std::overflow_error("Divide by zero exception");
    return numerator / denominator;
}

int main (void) {
    int i = 42;

    try { i = intDivEx (10, 2); }
    catch (std::overflow_error e) {
        std::cout << e.what() << " -> ";
    }
    std::cout << i << std::endl;

    try { i = intDivEx (10, 0); }
    catch (std::overflow_error e) {
        std::cout << e.what() << " -> ";
    }
    std::cout << i << std::endl;

    return 0;
}

Это выводит:

5
Divide by zero exception -> 5

, и вы можете видеть, что оно выдает и ловит исключение для деления на ноль.


% эквивалент почти точно такой же:

// Integer remainder, catching divide by zero.

inline int intModEx (int numerator, int denominator) {
    if (denominator == 0)
        throw std::overflow_error("Divide by zero exception");
    return numerator % denominator;
}
15 голосов
/ 01 сентября 2014

Обновлено с комментариями из ExcessPhase

GCC (по крайней мере версия 4.8) позволит вам эмулировать это поведение:

#include <signal.h>
#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<void(int)> handler(
        signal(SIGFPE, [](int signum) {throw std::logic_error("FPE"); }),
        [](__sighandler_t f) { signal(SIGFPE, f); });

    int i = 0;

    std::cin >> i;  // what if someone enters zero?

    try {
        i = 5/i;
    }
    catch (std::logic_error e) {
        std::cerr << e.what();
    }
}

Это устанавливает новый обработчик сигналакоторый генерирует исключение и shared_ptr для старого обработчика сигнала с пользовательской функцией «удаления», которая восстанавливает старый обработчик, когда он выходит из области видимости.

Вам необходимо скомпилировать хотя бы эти параметры:

g++ -c Foo.cc -o Foo.o -fnon-call-exceptions -std=c++11

Visual C ++ также позволит вам сделать что-то подобное:

#include <eh.h>
#include <memory>

int main() {
    std::shared_ptr<void(unsigned, EXCEPTION_POINTERS*)> handler(
        _set_se_translator([](unsigned u, EXCEPTION_POINTERS* p) {
            switch(u) {
                case FLT_DIVIDE_BY_ZERO:
                case INT_DIVIDE_BY_ZERO:
                    throw std::logic_error("Divide by zero");
                    break;
                ...
                default:
                    throw std::logic_error("SEH exception");
            }
        }),
        [](_se_translator_function f) { _set_se_translator(f); });

    int i = 0;

    try {
        i = 5 / i;
    } catch(std::logic_error e) {
        std::cerr << e.what();
    }
}

И, конечно, вы можете пропустить все это на C ++ 11 и поместить их втрадиционная RAII-управляющая структура.

7 голосов
/ 25 мая 2011

Насколько мне известно, в спецификациях C ++ ничего не говорится о делении на ноль, за исключением.Я полагаю, что вам нужно сделать это самостоятельно ...

Страуструп говорит в «Проектировании и развитии C ++» (Addison Wesley, 1994), «события низкого уровня, такие как арифметические переполнения иделение на ноль, как предполагается, обрабатывается специальным механизмом более низкого уровня, а не исключениями. Это позволяет C ++ соответствовать поведению других языков, когда дело доходит до арифметики. Это также позволяет избежать проблем, возникающих в сильно конвейерных архитектурах, где событиятакие как деление на ноль, являются асинхронными. "`

1 голос
/ 08 июня 2015

Вам нужно вызвать исключение вручную, используя ключевое слово throw.

Пример:

#include <iostream>
using namespace std;

double division(int a, int b)
{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}

int main ()
{
   int x = 50;
   int y = 0;
   double z = 0;

   try {
     z = division(x, y);
     cout << z << endl;
   }catch (const char* msg) {
     cerr << msg << endl;
   }

   return 0;
}
1 голос
/ 25 мая 2011

Вы должны проверить, если i = 0, а не делить тогда.

(При желании после проверки вы можете выбросить исключение и обработать его позже).

Больше информации по адресу: http://www.cprogramming.com/tutorial/exceptions.html

0 голосов

setjmp + longjmp

https://stackoverflow.com/a/25601100/895245 упомянул возможность или выдать исключение C ++ из обработчика сигнала, но Вызов исключения из обработчика сигнала упоминает несколько предостережений об этом, поэтому я буду очень осторожен.

В качестве другой потенциально опасной возможности вы также можете попытаться использовать более старый механизм C setjmp + longjmp, как показано на: C обработать сигнал SIGFPE и продолжить выполнение

#include <csetjmp>
#include <csignal>
#include <cstring>
#include <iostream>

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

Скомпилируйте и запустите:

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

Выход:

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

man longjmp говорит, что вы можете longjmp из обработчиков сигналов, но с несколькими оговорками:

Техническое исправление POSIX.1-2008 2 добавляет longjmp () и siglongjmp () в список функций, безопасных для асинхронных сигналов. Тем не менее, стандарт рекомендует избегать использования этих функций от обработчиков сигналов и продолжает указывать Выяснилось, что если эти функции вызываются из обработчика сигнала, который прервал вызов не асинхронно-безопасной функции сигнала (или некоторого эквивалента, такого как шаги, эквивалентные exit (3), которые происходят после возврата из начального вызов main ()), поведение не определено, если программа впоследствии выполняет вызов не асинхронно безопасной для сигнала функции. Единственный способ избежать неопределенного поведения - обеспечить одно из следующих действий:

  • После долгого перехода из обработчика сигнала программа не вызывает никаких функций, не поддерживающих асинхронный сигнал, и не возвращает исходный вызов main ().

  • Любой сигнал, обработчик которого выполняет прыжок в длину, должен блокироваться при каждом вызове функции, не поддерживающей асинхронный сигнал, и никакие функции, не поддерживающие асинхронный сигнал, не вызываются после возврата из первоначального вызова в main ().

См. Также: Longjmp вне обработчика сигнала?

Однако Вызывая исключение из обработчика сигнала упоминает, что это еще более опасно для C ++:

setjmp и longjmp не совместимы с исключениями и RAII (ctors / dtors). :( Возможно, вы получите утечки ресурсов с этим.

так что вы должны быть очень, очень осторожны с этим.

Полагаю, мораль в том, что обработчики сигналов сложны, и вам следует избегать их как можно чаще, если вы точно не знаете, что делаете.

Обнаружение с плавающей точкой с нулевым делением

Также возможно обнаружить деление с плавающей запятой на ноль с помощью вызова glibc:

#include <cfenv>

feenableexcept(FE_INVALID);

как показано на: В чем разница между тихим NaN и сигнальным NaN?

Это вызывает повышение SIGFPE, а также целочисленное деление на ноль, а не просто молча qnan и установка флагов.

0 голосов
/ 15 июля 2012

Вы можете просто сделать assert(2 * i != i), который выдаст утверждение. Вы можете написать свой собственный класс исключений, если вам нужно что-то более необычное.

0 голосов
/ 25 мая 2011

do i need to throw an exception or does the computer automatically throws one at runtime?

Либо вам нужно throw исключение самостоятельно и catch оно.например,

try {
  //...
  throw int();
}
catch(int i) { }

или catch исключение, которое выдается вашим кодом.

try {
    int *p = new int();
}
catch (std::bad_alloc e) {
    cerr << e.what();
}

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

catch(...) {  // catch 'any' exception
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...