Es gibt Fragen zur Verwendung exit
in C ++. In der Antwort wird diskutiert, dass es hauptsächlich aufgrund von RAII keine gute Idee ist, z. B. wenn exit
irgendwo im Code aufgerufen wird, werden Destruktoren von Objekten nicht aufgerufen. Wenn beispielsweise ein Destruktor Daten in eine Datei schreiben sollte, geschieht dies nicht , weil der Destruktor nicht aufgerufen wurde.
Ich war interessiert, wie ist diese Situation in C. Sind ähnliche Probleme auch in C anwendbar? Ich dachte, da wir in C keine Konstruktoren / Destruktoren verwenden, könnte die Situation in C anders sein. Ist es also in Ordnung, exit
in C zu verwenden?
Ich habe Funktionen wie die folgenden gesehen, die ich in einigen Fällen gut gebrauchen kann, war aber interessiert, ob wir in C ähnliche Probleme mit der Verwendung haben exit
, wie oben mit C ++ beschrieben? (was die Verwendung von Funktionen wie unten keine gute Idee machen würde.)
void die(const char *message)
{
if(errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
SIGTERM
Signals unter POSIX und Linux. Von gut erzogenen Servern wird erwartet, dass sie gut damit umgehen. Und Sie sollten es vermeidenSIGKILL
(dh versuchen Sie eskill -TERM
dannkill -QUIT
und erst späterkill -KILL
als Systemadministrator)exit()
Funktionen mit hohen Stimmen ist nicht von Belang (und die hohe Stimmenzahl ist überraschend - es ist keine gute Frage). Warum sollte ich die Funktion exit () in C nicht verwenden? wäre ein guter Kandidat, wenn es Antworten vom Kaliber dieser Frage hätte. Es tut nicht; Ein umgekehrter Abschluss davon als Duplikat davon ist angemessen - und ich habe es getan.Antworten:
Stattdessen
abort()
wird dieexit()
Funktion in C als "anmutiger" Ausgang betrachtet.Ab C11 (N1570) 7.22.4.4/p2 Die Exit-Funktion (Hervorhebung meiner):
Der Standard sagt auch in 7.22.4.4/p4, dass:
Es lohnt sich auch einen Blick auf 7.21.3 / p5- Dateien zu werfen :
Wie in den Kommentaren unten erwähnt, können Sie jedoch nicht davon ausgehen, dass alle anderen Ressourcen abgedeckt werden. Daher müssen Sie möglicherweise auf
atexit()
Rückrufe für deren Freigabe zurückgreifen und diese individuell definieren. Tatsächlich ist es genau das, wasatexit()
beabsichtigt ist, wie es in 7.22.4.2/p2 heißt. Die atexit-Funktion :Insbesondere sagt der C-Standard nicht genau vor, was mit Objekten mit zugeordneter Speicherdauer (dh
malloc()
) geschehen soll , sodass Sie wissen müssen, wie dies bei einer bestimmten Implementierung geschieht. Für moderne, hostorientierte Betriebssysteme ist es wahrscheinlich, dass sich das System darum kümmert, aber dennoch möchten Sie dies möglicherweise selbst erledigen, um Speicher-Debugger wie Valgrind zum Schweigen zu bringen .quelle
stdio
, dh einemclose()
auf dem FD entspricht. Ich weiß nicht, in welchem Sinne @BlueMoon das für falsch hält, aber es ist nicht weniger falsch als ein Anruf beiclose()
, und wenn weitere Aufklärung erforderlich ist, ist genau das der Zweckatexit()
.atexit()
sind. Mitexit()
selbst ist nicht brutaler als tunreturn
ausmain()
.atexit
, andernfalls ist dies in Ordnung. @BlueMoon: Ich glaube nicht, dass man Funktionen verwendendie
kann, die nicht programmieren können. In einigen Fällen möchte man sie möglicherweise verwenden. Andernfalls können Sie dies auch nur mit Rückgabewerten usw. behandelnJa, es ist in Ordnung,
exit
in C zu verwenden .Um alle Puffer und ein ordnungsgemäßes Herunterfahren zu gewährleisten, wird empfohlen, diese Funktion zu verwenden
atexit
. Weitere Informationen hierzu finden Sie hierEin Beispielcode wäre wie folgt:
void cleanup(void){ /* example of closing file pointer and free up memory */ if (fp) fclose(fp); if (ptr) free(ptr); } int main(int argc, char **argv){ /* ... */ atexit(cleanup); /* ... */ return 0; }
Jetzt wird bei jedem
exit
Aufruf die Funktioncleanup
ausgeführt, die ein ordnungsgemäßes Herunterfahren, Bereinigen von Puffern, Speicher usw. ermöglichen kann.quelle
Sie haben keine Konstruktoren und Destruktoren, aber Sie könnten Ressourcen (z. B. Dateien, Streams, Sockets) haben, und es ist wichtig, diese korrekt zu schließen. Ein Puffer konnte nicht synchron geschrieben werden. Wenn Sie das Programm verlassen, ohne die Ressource zuerst korrekt zu schließen, kann dies zu einer Beschädigung führen.
quelle
exit()
(anstatt_exit()
) aufrufen, werden dieatexit
Routinen aufgerufen und diestdio
Pufferung auf die Festplatte geleert.exit()
ist genau da, um ein ordentliches Beenden des Programms zu ermöglichen.atexit
können helfen, obwohl sie nicht immer angemessen sind.exit
wenn Sie möchten, aber es ist ein globaler Kontrollsprung, der mit allen Nachteilen der unstrukturierten Kontrolle einhergeht, die wir besprochen haben. Um von einer Fehlerbedingung zu beenden, kann dies angemessen sein (und es scheint das zu sein, was das OP will), obwohl ich es für einen normalen Kontrollfluss wahrscheinlich vorziehen würde, die Hauptschleife mit einer geeigneten Ausgangsbedingung zu entwerfen, damit Sie tatsächlich enden Rückkehr von der Hauptleitung.Die Verwendung
exit()
ist in OrdnungZwei Hauptaspekte des Code-Designs, die noch nicht erwähnt wurden, sind "Threading" und "Bibliotheken".
In einem Single-Thread-Programm ist die Verwendung in dem Code, den Sie zur Implementierung dieses Programms schreiben, in
exit()
Ordnung. Meine Programme verwenden es routinemäßig, wenn etwas schief gelaufen ist und der Code nicht wiederhergestellt werden kann.Aber…
Das Anrufen
exit()
ist jedoch eine einseitige Aktion, die nicht rückgängig gemacht werden kann. Aus diesem Grund erfordern sowohl "Threading" als auch "Bibliotheken" sorgfältige Überlegungen.Threaded-Programme
Wenn ein Programm über mehrere Threads verfügt, ist die Verwendung
exit()
eine dramatische Aktion, bei der alle Threads beendet werden. Es wird wahrscheinlich unangemessen sein, das gesamte Programm zu beenden. Es kann angebracht sein, den Thread zu beenden und einen Fehler zu melden. Wenn Sie mit dem Design des Programms vertraut sind, ist dieser einseitige Ausstieg möglicherweise zulässig, aber im Allgemeinen nicht akzeptabel.Bibliothekscode
Und diese Klausel „Kenntnis des Programmdesigns“ gilt auch für Code in Bibliotheken. Es ist sehr selten richtig, eine Allzweckbibliotheksfunktion aufzurufen
exit()
. Sie wären zu Recht verärgert, wenn eine der Standardfunktionen der C-Bibliothek nur aufgrund eines Fehlers nicht zurückgegeben werden könnte. (Offensichtlich Funktionen wieexit()
,_Exit()
,quick_exit()
,abort()
soll nicht auf Rückkehr, das anders ist.) Die Funktionen in der C - Bibliothek daher entweder „nicht versagen“ oder eine Fehleranzeige zurückzukehren irgendwie. Wenn Sie Code schreiben, um in eine Allzweckbibliothek zu gelangen, müssen Sie die Fehlerbehandlungsstrategie für Ihren Code sorgfältig abwägen. Es sollte zu den Fehlerbehandlungsstrategien der Programme passen, mit denen es verwendet werden soll, oder die Fehlerbehandlung kann konfigurierbar gemacht werden.Ich habe eine Reihe von Bibliotheksfunktionen (in einem Paket mit Header
"stderr.h"
, einem Namen, der auf dünnem Eis steht), die beendet werden sollen, wenn sie für die Fehlerberichterstattung verwendet werden. Diese Funktionen werden standardmäßig beendet. Es gibt eine verwandte Reihe von Funktionen im selben Paket, die Fehler melden und nicht beendet werden. Die vorhandenen Funktionen werden natürlich in Bezug auf die nicht vorhandenen Funktionen implementiert, aber das ist ein internes Implementierungsdetail.Ich habe viele andere Bibliotheksfunktionen, und viele von ihnen verlassen sich auf den
"stderr.h"
Code für die Fehlerberichterstattung. Das ist eine Designentscheidung, die ich getroffen habe und mit der ich einverstanden bin. Wenn die Fehler jedoch mit den Funktionen gemeldet werden, die beendet werden, wird der allgemeine Nutzen des Bibliothekscodes eingeschränkt. Wenn der Code die Fehlerberichtsfunktionen aufruft, die nicht beendet werden, müssen die Hauptcodepfade in der Funktion Fehlerrückgaben ordnungsgemäß behandeln - erkennen Sie sie und leiten Sie eine Fehleranzeige an den aufrufenden Code weiter.Der Code für mein Fehlerberichtspaket ist in meinem SOQ- Repository (Stack Overflow Questions) auf GitHub als Dateien
stderr.c
undstderr.h
im Unterverzeichnis src / libsoq verfügbar .quelle
abort()
wenn Speicher zugewiesen werden soll,malloc()
oder stellenrealloc()
Sie sich vor, Sie haben eine Anwendung, die mit 100 Bibliotheken verknüpft ist, und Sie fragen sich, welche und wie Ihre Anwendung abgestürzt ist. Außerdem habe ichabort()
in ihrer Dokumentation keine Erwähnung gefunden (aber versteh mich nicht falsch. Es ist eine großartige Bibliothek für diesen Zweck).stderr
ist normalerweise zeilengepuffert. Wenn die Ausgabe mit einem Zeilenumbruch endet, wird sie trotzdem vom System geleert.Ein Grund, den Sie
exit
bei anderen Funktionen als vermeiden sollten,main()
ist die Möglichkeit, dass Ihr Code aus dem Kontext gerissen wird. Denken Sie daran, dass exit eine Art nicht lokaler Kontrollfluss ist . Wie unauffindbare Ausnahmen.Beispielsweise können Sie einige Speicherverwaltungsfunktionen schreiben, die bei einem kritischen Festplattenfehler beendet werden. Dann beschließt jemand, sie in eine Bibliothek zu verschieben. Das Verlassen einer Bibliothek führt dazu, dass das aufrufende Programm in einem inkonsistenten Zustand beendet wird, auf den es möglicherweise nicht vorbereitet ist.
Oder Sie können es auf einem eingebetteten System ausführen. Es gibt nirgendwo zu verlassen , um das Ganze läuft in einer
while(1)
in Schleifemain()
. Es ist möglicherweise nicht einmal in der Standardbibliothek definiert.quelle
Je nachdem, was Sie tun, ist das Beenden möglicherweise der logischste Ausweg aus einem Programm in C. Ich weiß, dass es sehr nützlich ist, um zu überprüfen, ob Rückrufketten ordnungsgemäß funktionieren. Nehmen Sie diesen Beispiel-Rückruf, den ich kürzlich verwendet habe:
unsigned char cbShowDataThenExit( unsigned char *data, unsigned short dataSz,unsigned char status) { printf("cbShowDataThenExit with status %X (dataSz %d)\n", status, dataSz); printf("status:%d\n",status); printArray(data,dataSz); cleanUp(); exit(0); }
In der Hauptschleife richte ich alles für dieses System ein und warte dann in einer Weile (1). Es ist möglich, stattdessen ein globales Flag zu erstellen, um die while-Schleife zu verlassen. Dies ist jedoch einfach und macht das, was es tun muss. Wenn Sie mit offenen Puffern wie Dateien und Geräten arbeiten, sollten Sie diese aus Gründen der Konsistenz vor dem Schließen bereinigen.
quelle
In einem großen Projekt ist es schrecklich, wenn jeder Code außer Coredump beendet werden kann. Trace ist sehr wichtig, um einen Online-Server zu warten.
quelle