web-dev-qa-db-fra.com

Exception capturante: diviser par zéro

Le code suivant n'intercepte pas une exception lorsque j'essaie de diviser par 0. Dois-je lever une exception ou l'ordinateur en envoie-t-il une automatiquement au moment de l'exécution?

int i = 0;

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

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

    cerr << e.what();
}
30
user33424

Vous devez vérifier vous-même et lancer une exception. La division entière par zéro n'est pas une exception en C++ standard.

La division en virgule flottante n'est pas non plus une division par zéro, mais au moins cela a des moyens spécifiques pour y faire face.

Les exceptions répertoriées dans la norme ISO sont les suivantes:

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

et vous penseriez que overflow_error serait idéal pour indiquer une division par zéro.

Mais la section 5.6 (de C++11, bien que je ne pense pas que cela ait changé par rapport à l'itération précédente) indique spécifiquement:

Si le deuxième opérande de / ou % est égal à zéro, le comportement est indéfini.

Donc, il pourrait jeter cette exception (ou toute autre). Il pourrait également formater votre disque dur et rire de façon dérisoire :-)


Si vous voulez implémenter une telle bête, vous pouvez utiliser quelque chose comme intDivEx dans le programme suivant:

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

Cela génère:

5
Divide by zero exception -> 5

et vous pouvez voir qu'il jette et attrape l'exception pour le cas de division par zéro.


L'équivalent % est presque exactement le même:

// 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;
}
50
paxdiablo

Mis à jour avec les commentaires d'ExcessPhase

GCC (au moins la version 4.8) vous permettra d'imiter ce comportement:

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

Ceci configure un nouveau gestionnaire de signal qui lève une exception et un shared_ptr à l'ancien gestionnaire de signal, avec une fonction personnalisée de «suppression» qui restaure l'ancien gestionnaire lorsqu'il est hors de portée.

Vous devez compiler avec au moins ces options:

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

Visual C++ vous permettra également de faire quelque chose de similaire:

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

Et bien sûr, vous pouvez ignorer toute la complexité de C++ 11 et les placer dans une structure de gestion RAII traditionnelle.

12
Tom

Autant que je sache, les spécifications C++ ne mentionnent rien sur la division par zéro. Je crois que vous devez le faire vous-même ... 

Selon Stroustrup, dans "Conception et évolution du C++" (Addison Wesley, 1994), "les événements de niveau inférieur, tels que les débordements arithmétiques et la division par zéro, sont supposés être gérés par un mécanisme dédié de niveau inférieur plutôt que par des exceptions. Cela permet au C++ de s’adapter au comportement d’autres langages en matière d’arithmétique. Cela évite également les problèmes qui se produisent sur les architectures très en pipeline où des événements tels que la division par zéro sont asynchrones. "`

7
Mayank

Vous devriez vérifier si i = 0 et ne pas diviser alors.

(Eventuellement, après vérification, vous pouvez lever une exception et la gérer plus tard).

Plus d'infos sur: http://www.cprogramming.com/tutorial/exceptions.html

1
Vadiklk

Vous devez lever l'exception manuellement à l'aide du mot clé throw.

Exemple:

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