Ich habe heute über die Try / Catch-Blöcke nachgedacht, die in anderen Sprachen existieren. Googelte eine Weile, aber ohne Ergebnis. Soweit ich weiß, gibt es in C kein Try / Catch. Gibt es jedoch eine Möglichkeit, sie zu "simulieren"?
Klar, es gibt Assert und andere Tricks, aber nichts wie try / catch, die auch die ausgelöste Ausnahme abfangen. Danke dir
101
Antworten:
C selbst unterstützt keine Ausnahmen, aber Sie können sie bis zu einem gewissen Grad mit
setjmp
undlongjmp
aufrufen.Diese Website enthält ein nettes Tutorial zum Simulieren von Ausnahmen mit
setjmp
undlongjmp
quelle
try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')};
es nicht richtig?Sie verwenden goto in C für ähnliche Fehlerbehandlungssituationen.
Dies ist das nächste Äquivalent zu Ausnahmen, die Sie in C erhalten können.
quelle
goto
eher zur Fehlerbehandlung verwendet, aber was nun? Die Frage bezieht sich nicht auf die Fehlerbehandlung als solche, sondern explizit auf Try / Catch-Äquivalente.goto
ist kein Äquivalent für try / catch, da es auf dieselbe Funktion beschränkt ist.goto
als Try / Catch-Mechanismus, der in einer modernen, allgemein akzeptierten, von Experten geprüften Quelle verwendet wird. Suchen Siegoto
nach einem "Wurf" -Äquivalent undfinish
nach einem "Fang" -Äquivalent.Ok, ich konnte nicht widerstehen, darauf zu antworten. Lassen Sie mich zunächst sagen, dass ich es nicht für eine gute Idee halte, dies in C zu simulieren, da es für C wirklich ein Fremdwort ist.
Wir können
verwendendie Prä - Prozessor und lokalen Stack - Variablen missbrauchen Verwendung eine eingeschränkte Version von C ++ try / throw / catch zu geben.Version 1 (lokaler Gültigkeitsbereich wirft)
Version 1 ist nur ein lokaler Wurf (kann den Funktionsumfang nicht verlassen). Es hängt von der Fähigkeit von C99 ab, Variablen im Code zu deklarieren (es sollte in C89 funktionieren, wenn der Versuch als erstes in der Funktion ist).
Diese Funktion erstellt nur eine lokale Variable, damit sie weiß, ob ein Fehler aufgetreten ist, und springt mit einem goto zum catch-Block.
Beispielsweise:
Das funktioniert wie folgt:
Version 2 (Scope Jumping)
Version 2 ist viel komplexer, funktioniert aber grundsätzlich genauso. Es wird ein langer Sprung von der aktuellen Funktion zum try-Block verwendet. Der try-Block verwendet dann ein if / else, um den Codeblock zum catch-Block zu überspringen, der die lokale Variable überprüft, um festzustellen, ob sie abfangen soll.
Das Beispiel wurde erneut erweitert:
Dies verwendet einen globalen Zeiger, damit longjmp () weiß, welcher Versuch zuletzt ausgeführt wurde. Wir
verwendenden Stapel, damit untergeordnete Funktionen auch einen Try / Catch-Block haben können.Die Verwendung dieses Codes hat eine Reihe von Nachteilen (ist aber eine lustige mentale Übung):
quelle
bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}
. Die lokale Variable würde aber auch neu definiert, sodass die Dinge etwas außer Kontrolle geraten.In C99 könnenSiesetjmp
/longjmp
für den nicht lokalen Kontrollfluss verwenden.Innerhalb eines einzelnen Bereichs wird das generische, strukturierte Codierungsmuster für C bei Vorhandensein mehrerer Ressourcenzuweisungen und mehrerer Exits
goto
wie in diesem Beispiel verwendet . Dies ähnelt der Implementierung von Destruktoraufrufen von automatischen Objekten unter der Haube. Wenn Sie sich sorgfältig daran halten, sollte dies auch bei komplexen Funktionen ein gewisses Maß an Sauberkeit ermöglichen.quelle
Während einige der anderen Antworten die einfachen Fälle mit
setjmp
und behandelt habenlongjmp
, gibt es in einer realen Anwendung zwei Bedenken, die wirklich wichtig sind.jmp_buf
, funktionieren diese nicht.jmp_buf
verursacht in dieser Situation alle Arten von Schmerzen.Die Lösung für diese besteht darin, einen threadlokalen Stapel von zu verwalten
jmp_buf
, der im Laufe der Zeit aktualisiert wird. (Ich denke, das ist es, was Lua intern verwendet).Also stattdessen (aus JaredPars großartiger Antwort)
Sie würden so etwas wie verwenden:
Wiederum würde eine realistischere Version davon eine Möglichkeit beinhalten, Fehlerinformationen in der zu speichern
exception_state
, besseren Handhabung vonMAX_EXCEPTION_DEPTH
(möglicherweise Realloc verwenden, um den Puffer zu vergrößern, oder so ähnlich).HAFTUNGSAUSSCHLUSS: Der obige Code wurde ohne jegliche Prüfung geschrieben. Es ist nur so, dass Sie eine Vorstellung davon bekommen, wie man Dinge strukturiert. Unterschiedliche Systeme und unterschiedliche Compiler müssen den lokalen Thread-Speicher unterschiedlich implementieren. Der Code enthält wahrscheinlich sowohl Kompilierungsfehler als auch Logikfehler. Wenn Sie ihn also nach Belieben verwenden können, testen Sie ihn, bevor Sie ihn verwenden.
quelle
Eine schnelle Google-Suche liefert kludgey Lösungen wie diese , die setjmp / longjmp verwenden, wie andere erwähnt haben. Nichts ist so einfach und elegant wie C ++ / Java's try / catch. Ich bin ziemlich angetan von Adas Ausnahmebehandlung.
Überprüfen Sie alles mit if-Anweisungen :)
quelle
Dies ist
setjmp/longjmp
in C möglich. P99 verfügt über ein recht komfortables Toolset, das auch mit dem neuen Thread-Modell von C11 übereinstimmt.quelle
Dies ist eine weitere Möglichkeit zur Fehlerbehandlung in C, die leistungsfähiger ist als die Verwendung von setjmp / longjmp. Leider funktioniert es nicht mit MSVC, aber wenn nur die Verwendung von GCC / Clang eine Option ist, können Sie dies in Betracht ziehen. Insbesondere wird die Erweiterung "Label als Wert" verwendet, mit der Sie die Adresse eines Labels übernehmen, in einem Wert speichern und bedingungslos dorthin springen können. Ich werde es anhand eines Beispiels präsentieren:
Wenn Sie möchten, können Sie allgemeinen Code in Definitionen umgestalten und so Ihr eigenes Fehlerbehandlungssystem effektiv implementieren.
Dann wird das Beispiel
quelle
Warnung: Das Folgende ist nicht sehr schön, aber es macht den Job.
Verwendung:
Ausgabe:
Beachten Sie, dass hierfür verschachtelte Funktionen und verwendet werden
__COUNTER__
. Sie sind auf der sicheren Seite, wenn Sie gcc verwenden.quelle
Redis simulieren mit goto try / catch. IMHO ist es sehr sauber und elegant:
quelle
errno
darf nur unmittelbar nach dem fehlgeschlagenen Systemaufruf und nicht drei Aufrufe später verwendet werden.In C können Sie Ausnahmen zusammen mit der automatischen "Objektrückgewinnung" durch manuelle Verwendung von if + goto für die explizite Fehlerbehandlung "simulieren".
Ich schreibe oft C-Code wie den folgenden (zusammengefasst, um die Fehlerbehandlung hervorzuheben):
Dies ist völlig normales ANSI C, trennt die Fehlerbehandlung von Ihrem Hauptzeilencode, ermöglicht das (manuelle) Abwickeln von initialisierten Objekten wie in C ++ und es ist völlig offensichtlich, was hier passiert. Da Sie an jedem Punkt explizit auf Fehler testen, ist es einfacher, an jeder Stelle, an der ein Fehler auftreten kann, eine bestimmte Protokollierung oder Fehlerbehandlung einzufügen.
Wenn Ihnen ein wenig Makromagie nichts ausmacht, können Sie dies präziser gestalten, während Sie andere Dinge wie das Protokollieren von Fehlern mit Stapelspuren ausführen. Beispielsweise:
Dies ist natürlich nicht so elegant wie C ++ - Ausnahmen + Destruktoren. Das Verschachteln mehrerer Fehlerbehandlungsstapel in einer Funktion auf diese Weise ist beispielsweise nicht sehr sauber. Stattdessen möchten Sie diese wahrscheinlich in eigenständige Unterfunktionen aufteilen, die Fehler auf ähnliche Weise behandeln. Initialisieren und finalisieren Sie dies explizit.
Dies funktioniert auch nur innerhalb einer einzelnen Funktion und springt nicht weiter in den Stapel, es sei denn, Aufrufer höherer Ebenen implementieren eine ähnliche explizite Fehlerbehandlungslogik, während eine C ++ - Ausnahme den Stapel nur so lange hochspringt, bis ein geeigneter Handler gefunden wird. Sie können auch keinen beliebigen Typ auslösen, sondern nur einen Fehlercode.
Das systematische Codieren auf diese Weise (dh mit einem einzelnen Eintritts- und einem einzigen Austrittspunkt) macht es auch sehr einfach, Pre- und Post-Logik ("endlich") einzufügen, die unabhängig von der jeweiligen Ausführung ausgeführt wird. Sie setzen einfach Ihre "Endlich" -Logik nach dem END-Label.
quelle
Wenn Sie C mit Win32 verwenden, können Sie das Structured Exception Handling (SEH) nutzen , um Try / Catch zu simulieren.
Wenn Sie C auf Plattformen verwenden, die dies nicht unterstützen,
setjmp()
undlongjmp()
sich diese Ausnahmebehandlung der pjsip-Bibliothek ansehen, bietet sie eine eigene Implementierungquelle
Vielleicht keine Hauptsprache (leider), aber in APL gibt es die ⎕EA-Operation (steht für Execute Alternate).
Verwendung: 'Y' ⎕EA 'X' wobei X und Y entweder Codefragmente sind, die als Zeichenfolgen oder Funktionsnamen geliefert werden.
Wenn X auf einen Fehler stößt, wird stattdessen Y (normalerweise Fehlerbehandlung) ausgeführt.
quelle