
Benutzer33424
Der folgende Code fängt keine Ausnahme ab, wenn ich versuche, durch 0 zu teilen. Muss ich eine Ausnahme auslösen, oder löst der Computer zur Laufzeit automatisch eine Ausnahme aus?
int i = 0;
cin >> i; // what if someone enters zero?
try {
i = 5/i;
}
catch (std::logic_error e) {
cerr << e.what();
}

paxdiablo
Sie müssen es selbst überprüfen und eine Ausnahme auslösen. Ganzzahlige Division durch Null ist keine Ausnahme in Standard-C++.
Es ist auch keine Gleitkommadivision durch Null, aber zumindest hat das spezielle Mittel, um damit umzugehen.
Die in der ISO-Norm aufgeführten Ausnahmen sind:
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;
}
und das könnte man auch ziemlich überzeugend argumentieren overflow_error
(Die von IEEE754-Gleitkomma erzeugte Unendlichkeit könnte als Überlauf angesehen werden) oder domain_error
(es ist ein Problem mit dem Eingabewert) wäre ideal, um eine Division durch Null anzuzeigen.
Abschnitt jedoch 5.6
(von C++11
obwohl ich nicht glaube, dass sich dies gegenüber der vorherigen Iteration geändert hat) heißt es ausdrücklich:
Wenn der zweite Operand von /
oder %
Null ist, ist das Verhalten undefiniert.
So dass es könnten Werfen Sie diese (oder andere) Ausnahmen aus. Es könnte auch Ihre Festplatte formatieren und höhnisch lachen 🙂
Wenn Sie ein solches Biest implementieren wollten, könnten Sie so etwas wie verwenden intDivEx
im folgenden Programm (unter Verwendung der Überlaufvariante):
#include <iostream>
#include <stdexcept>
// Integer division/remainder, 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;
}
inline int intModEx (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, 0); }
catch (std::overflow_error &e) {
std::cout << e.what() << " -> ";
}
std::cout << i << std::endl;
try { i = intDivEx (10, 2); }
catch (std::overflow_error &e) {
std::cout << e.what() << " -> ";
}
std::cout << i << std::endl;
return 0;
}
Dies gibt aus:
Divide by zero exception -> 42
5
und Sie können sehen, dass es die Ausnahme für den Fall der Division durch Null auslöst und abfängt (wobei die Rückgabevariable unberührt bleibt).
Die %
Äquivalent ist fast genau dasselbe:

Tom
Aktualisiert mit Kommentaren von ExcessPhase
Mit GCC (mindestens Version 4.8) können Sie dieses Verhalten emulieren:
#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();
}
}
Dies richtet einen neuen Signalhandler ein, der eine Ausnahme auslöst, und a shared_ptr
zum alten Signal-Handler, mit einer benutzerdefinierten ‘Lösch’-Funktion, die den alten Handler wiederherstellt, wenn er den Gültigkeitsbereich verlässt.
Sie müssen mindestens mit diesen Optionen kompilieren:
g++ -c Foo.cc -o Foo.o -fnon-call-exceptions -std=c++11
Mit Visual C++ können Sie auch etwas Ähnliches tun:
#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();
}
}
Und natürlich können Sie den ganzen C++11-Stil überspringen und sie in eine traditionelle RAII-Verwaltungsstruktur stecken.

Mayank
Soweit ich weiß, erwähnen die C++-Spezifikationen nichts über die Ausnahme der Division durch Null. ich glaub das musst du selber machen…
Stroustrup sagt in “The Design and Evolution of C++” (Addison Wesley, 1994): “Low-Level-Ereignisse wie arithmetische Überläufe und Division durch Null werden eher von einem dedizierten Mechanismus auf niedrigerer Ebene als von Ausnahmen behandelt . Dies ermöglicht es C++, sich dem Verhalten anderer Sprachen in Bezug auf Arithmetik anzupassen. Es vermeidet auch die Probleme, die bei stark Pipeline-Architekturen auftreten, bei denen Ereignisse wie das Teilen durch Null asynchron sind.”`
Sie müssen die Ausnahme manuell mit auslösen throw
Stichwort.
Beispiel:
#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;
}

Wadiklk
Sie sollten prüfen, ob i = 0
und dann nicht teilen.
(Optional können Sie nach der Überprüfung eine Ausnahme auslösen und später behandeln).
Mehr Infos unter: http://www.cprogramming.com/tutorial/exceptions.html
setjmp
+ longjmp
https://stackoverflow.com/a/25601100/895245 erwähnte die Möglichkeit oder das Auslösen einer C++-Ausnahme von einem Signalhandler, aber das Auslösen einer Ausnahme innerhalb eines Signalhandlers erwähnt mehrere Vorbehalte, also wäre ich sehr vorsichtig.
Als weitere potenziell gefährliche Möglichkeit können Sie auch versuchen, das ältere C setjmp
+ longjmp
Mechanismus wie unter: C handhaben Sie das Signal SIGFPE und setzen Sie die Ausführung fort
main.cpp
#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;
}
Kompilieren und ausführen:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
Ausgabe:
i = 0
before divide
after longjmp
i = 1
before divide
after longjmp
i = 2
before divide
after longjmp
man longjmp
sagt, dass Sie können longjmp
von Signalhandlern, aber mit einigen Einschränkungen:
POSIX.1-2008 Technical Corrigendum 2 fügt longjmp() und siglongjmp() zur Liste der Async-Signal-sicheren Funktionen hinzu. Der Standard empfiehlt jedoch, die Verwendung dieser Funktionen von Signal-Handlern zu vermeiden, und weist weiter darauf hin, dass, wenn diese Funktionen von einem Signal-Handler aufgerufen werden, der einen Aufruf einer nicht-asynchronen signalsicheren Funktion (oder eines Äquivalents, wie z wie die zu exit(3) äquivalenten Schritte, die bei einer Rückkehr vom ursprünglichen Aufruf von main() auftreten), ist das Verhalten undefiniert, wenn das Programm anschließend eine nicht asynchronsignalsichere Funktion aufruft. Die einzige Möglichkeit, undefiniertes Verhalten zu vermeiden, besteht darin, Folgendes sicherzustellen:
-
Nach einem langen Sprung vom Signal-Handler ruft das Programm keine nicht-async-signalsicheren Funktionen auf und kehrt nicht vom ursprünglichen Aufruf zu main() zurück.
-
Jedes Signal, dessen Handler einen langen Sprung durchführt, muss bei jedem Aufruf einer nicht-asynchronen signalsicheren Funktion blockiert werden, und es werden keine nicht-asynchronen signalsicheren Funktionen nach der Rückkehr vom ursprünglichen Aufruf von main() aufgerufen.
Siehe auch: Longjmp aus Signalhandler?
Das Auslösen einer Ausnahme innerhalb eines Signalhandlers erwähnt jedoch, dass dies weitere Gefahren mit C++ birgt:
setjmp und longjmp sind jedoch nicht mit Ausnahmen und RAII (ctors/dtors) kompatibel. 🙁 Sie werden wahrscheinlich Ressourcenlecks bekommen.
also müsstest du auch damit sehr sehr vorsichtig sein.
Ich denke, die Moral ist, dass Signalhandler hart sind, und Sie sollten sie so weit wie möglich vermeiden, es sei denn, Sie wissen genau, was Sie tun.
Gleitkomma-Nulldivision erkennen
Es ist auch möglich, eine Gleitkommadivision durch Null mit einem glibc-Aufruf zu erkennen:
#include <cfenv>
feenableexcept(FE_INVALID);
wie gezeigt unter: Was ist der Unterschied zwischen stillem NaN und signalisierendem NaN?
Dadurch wird SIGFPE ebenso wie die Integer-Division durch Null erhöht, anstatt nur stillschweigend zu qnan und Flags zu setzen.

Damkrat
Was ist mit diesem? Mit Clang getestet, wirft GCC SIGILL.
#include <iostream>
#include <cassert>
int main()
{
unsigned int x = 42;
unsigned int y = x;
y -= x;
x /= y;
std::cout << x << " != "<< *(&x) << std::endl;
assert (*(&x) == x);
}
9882500cookie-checkAbfangende Ausnahme: Division durch Nullyes
Warum nicht einfach prüfen, ob
i
ist null oder nicht?– Nik
25. Mai 2011 um 8:34 Uhr
Warum versuchst du zu dividieren durch ze— OH SCHEI-
– BoltClock
25. Mai 2011 um 8:35 Uhr
Nein, nicht wirklich, ich habe meine Frage bearbeitet.
– Benutzer33424
25. Mai 2011 um 8:40 Uhr
Diese Frage und alle darauffolgenden Antworten sind eine gute Studie über alles, was mit Ausnahmen nicht stimmt.
– Carey Gregory
15. Oktober 2013 um 4:15 Uhr