return Anweisung vs exit () in main ()

197

Soll ich exit()oder nur returnAnweisungen in verwenden main()? Persönlich bevorzuge ich die returnAussagen, weil ich das Gefühl habe, eine andere Funktion zu lesen, und die Flusskontrolle beim Lesen des Codes reibungslos ist (meiner Meinung nach). Und selbst wenn ich die main()Funktion umgestalten möchte , returnscheint es eine bessere Wahl zu sein als exit().

Tut exit()etwas Besonderes, was returnnicht?

Srikanth
quelle

Antworten:

277

Eigentlich gibt es einen Unterschied, aber er ist subtil. Es hat mehr Auswirkungen auf C ++, aber die Unterschiede sind wichtig.

Wenn ich rufe returnin main()wird Destruktoren für meine lokal scoped Objekte aufgerufen werden. Wenn ich anrufeexit() , wird kein Destruktor für meine Objekte mit lokalem Gültigkeitsbereich aufgerufen! Lesen Sie das noch einmal. exit() kommt nicht zurück . Das heißt, wenn ich es einmal nenne, gibt es "keine Backsies". Alle Objekte, die Sie in dieser Funktion erstellt haben, werden nicht zerstört. Oft hat dies keine Auswirkungen, aber manchmal auch das Schließen von Dateien (möchten Sie sicher, dass alle Ihre Daten auf die Festplatte übertragen werden?).

Beachten Sie, dass staticObjekte auch dann aufgeräumt werden, wenn Sie anrufen exit(). Beachten Sie schließlich, dass bei Verwendung abort()keine Objekte zerstört werden. Das heißt, keine globalen Objekte, keine statischen Objekte und keine lokalen Objekte werden ihre Destruktoren aufgerufen.

Gehen Sie vorsichtig vor, wenn Sie die Ausfahrt der Rückkehr vorziehen.

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a

Freier Speicher
quelle
1
abort () wird mit einer Fehlerbedingung (Exit-Code ungleich Null) beendet und kann sogar ein Kern sein. Wenn Sie beenden müssen, ohne statische Destruktoren aufzurufen, verwenden Sie _exit.
7
@Mike: Es gibt einen Unterschied zwischen den C-Bibliotheksdateipuffern und den C ++ - Dateistreamobjekten. exit () - Teil der C-Bibliothek - wurde entwickelt, um mit der ersteren zu koordinieren und diese zu leeren, kann jedoch die letztere umgehen: Selbst Standard-C ++ - Fstream-Inhalte werden nicht auf die Festplatte geleert (versuchen Sie es - ich habe es getan, es schlägt mit Linux / fehl) GCC) und offensichtlich benutzerdefinierte Typen, die E / A gepuffert haben, können ebenfalls nicht erwartet werden.
Tony Delroy
9
Hinweis: Die Anweisung: Für meine Objekte mit lokalem Gültigkeitsbereich wird kein Destruktor aufgerufen! gilt nicht mehr für C ++ 11: - Objekte, die dem aktuellen Thread mit Thread-Speicherdauer zugeordnet sind, werden zerstört (nur C ++ 11). cplusplus.com/reference/cstdlib/exit
Ilendir
7
Dies bedeutet, dass thread_localdie Destruktoren von Objekten aufgerufen werden. Destruktoren für andere lokale Objekte werden immer noch nicht aufgerufen. ideone.com/Y6Dh3f
HolyBlackCat
3
Übrigens, und nur um pedantisch zu sein und weil diese Antwort für Leser, die C verwenden, immer noch verwirrend sein kann: Für C ist das Problem des exit()sauberen Schließens von Dateien tatsächlich falsch. Die einzige Zeit, in der Daten möglicherweise nicht gelöscht werden, ist im umgekehrten Fall: dh wenn man returnfrom verwendet main()und aufgerufen hat setbuf()oder wenn setvbuf()ein Puffer als automatische Speicherung in deklariert ist main()(wie in der Antwort von R. unten erläutert). Es ist wirklich schade, dass diese Frage sowohl mit C als auch mit C ++ gekennzeichnet ist (und mit Codierungsstil - es ist kein Stilproblem!).
Greg A. Woods
25

Ein weiterer Unterschied: Es exithandelt sich um eine Standardbibliotheksfunktion, sodass Sie Header einfügen und mit der Standardbibliothek verknüpfen müssen. Zur Veranschaulichung (in C ++) ist dies ein gültiges Programm:

int main() { return 0; }

Für die Verwendung exitbenötigen Sie jedoch ein Include:

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

Dies fügt eine zusätzliche Annahme hinzu: Das Anrufen exitvon mainhat die gleichen Nebenwirkungen wie das Zurückgeben von Null. Wie andere bereits betont haben, hängt dies davon ab, welche Art von ausführbarer Datei Sie erstellen (dh wer anruft main). Codieren Sie eine App, die die C-Laufzeit verwendet? Ein Maya-Plugin? Ein Windows-Dienst? Ein Fahrer? Jeder Fall erfordert Nachforschungen, um festzustellen, ob dies exitgleichwertig ist return. IMHO zu verwenden, exitwenn Sie wirklich meinen, return macht den Code nur verwirrender. OTOH, wenn du es wirklich meinst exit , dann benutze es auf jeden Fall.

jwfearn
quelle
16

Es gibt mindestens einen Grund, den Sie bevorzugen sollten exit: Wenn einer Ihrer atexitHandler auf Daten zur automatischen Speicherdauer in verweist mainoder wenn Sie einen Puffer mit automatischer Speicherdauer in einem der Standard-Streams verwendet setvbufoder diesem setbufzugewiesen haben main, kehren Sie von den mainErzeugnissen zurück undefiniertes Verhalten, aber Aufruf exitist gültig.

Eine andere mögliche Verwendung (normalerweise jedoch für Spielzeugprogramme reserviert) besteht darin, ein Programm mit rekursiven Aufrufen von zu verlassen main.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
1
@Seb es gibt nichts Besonderes main()- es ist nur eine Funktion wie jede andere. Auf der anderen Seite muss der Standard, da er in der Norm besonders erwähnt wird, ziemlich vorsichtig sein, wie er definiert main()und was ihm nahe und teuer ist. Letztendlich verlangt der Standard jedoch nicht (und darf auch nicht ), dass Compiler etwas Besonderes in Bezug auf die automatische Speicherung in tun main(). Bitte lesen Sie die Fußnote 11 unter dem Absatz, auf den Sie in Ihrem Kommentar verwiesen haben.
Greg A. Woods
1
@ GregA.Woods Interessant. Es scheint, dass es einen normativen Text gibt, der im Widerspruch zu einem informativen Text steht. Gemäß den ISO / IEC-Richtlinien wird die normative Referenz als "unverzichtbar" angesehen, da die Information nur als ergänzend angesehen wird. Außerdem ist die Verwendung des Wortes "Wille" zur Übermittlung einer Anforderung ungültig. gemäß dem oben genannten Dokument (Anhang H). Zusammenfassend ist der informative Text mit Sicherheit ungültig.
autistische
2
@Seb: Die Absicht ist offensichtlich, die Anforderungen an das Verhalten der automatischen Speicherung nicht außer Kraft zu setzen, und die Fußnote wurde offensichtlich geschrieben, um dies zu verdeutlichen. Ja, der C-Standard enthält ungenaue, schlechte Formulierungen. Jeder, der es gelesen hat, weiß das. Wir wissen auch, dass das Komitee solche Probleme im Allgemeinen nicht behebt, da die Absicht bereits offensichtlich ist.
R .. GitHub STOP HELPING ICE
1
@Seb: Dies ist keine Debatte oder Gerichtsverhandlung, um zu beweisen, dass Sie Recht haben. Das Ziel sollte darin bestehen, ein klares Verständnis der tatsächlichen C-Sprache (wie beabsichtigt und implementiert) zu erlangen und dies in Antworten auszudrücken, die für die Leser nützlich sind. Der normative Text ist subtil falsch (entgegen der Absicht dessen, was er ausdrücken sollte) in einer Weise, die im Wesentlichen durch die Fußnote festgelegt ist. Wenn Sie damit nicht zufrieden sind, senden Sie einen Fehlerbericht, aber erwarten Sie keine Antwort. So rollt WG14 ...
R .. GitHub STOP HELPING ICE
3
@Seb: Sie scheinen zu glauben, dass die C-Sprache verstanden werden kann, wenn Sie den natürlichsprachigen Text des Standards so interpretieren, als ob er völlig streng wäre. Das ist einfach nicht möglich. Die Spezifikation enthält Fehler, und WG14 verschwendet keine Zeit damit, Dinge neu zu schreiben, wenn eine einfache Fußnote klarstellt, dass sie bereits wissen, dass sie einen Fehler gemacht haben, der Leser dies jedoch verstehen kann.
R .. GitHub STOP HELPING ICE
5

Ich benutze immer, returnweil der Standardprototyp für main()sagt, dass er ein zurückgibt int.

Einige Versionen der Standards bieten jedoch eine mainSonderbehandlung und gehen davon aus, dass sie 0 zurückgeben, wenn keine explizite returnAussage vorliegt. Gegeben den folgenden Code:

int foo() {}
int main(int argc, char *argv[]) {}

G ++ generiert nur eine Warnung für foo()und ignoriert die fehlende Rückgabe von main:

% g++ -Wall -c foo.cc
foo.cc: In function int foo()’:
foo.cc:1: warning: control reaches end of non-void function
Alnitak
quelle
Ich weiß nichts über C, aber der C ++ - Standard gibt an, dass, wenn Sie keinen Wert in main zurückgeben, angenommen wird, dass er 0
Jason Baker
Es scheint, als ob C99 dasselbe ist: faq.cprogramming.com/cgi-bin/…
Jason Baker
2
C99 und C ++ geben 0 zurück, wenn keine return-Anweisung vorhanden ist, C90 nicht.
d0k
Nur weil eine Funktion als Rückgabewert deklariert ist, müssen Sie sie nicht verwenden return, um ihre Ausführung zu beenden. Das Aufrufen exit()ist auch eine gültige und manchmal notwendige Möglichkeit, die Ausführung einer Funktion zu beenden. Wie ich und andere an anderer Stelle beschrieben haben, vermittelt das Aufrufen von exit()sogar main()eine weitaus klarere Absicht, den gesamten Prozess zu beenden, die automatische Speicherung bis zum Beenden des Prozesses beizubehalten und die Wartung während des zukünftigen Refactorings von Code zu vereinfachen. Für C ist es daher wohl eine schlechte Praxis , returnin zu verwenden, main()wenn die Absicht besteht, den Prozess zu beenden.
Greg A. Woods
@ GregA.Woods das ist deine Meinung, verdient aber kaum eine Abwertung! Was ich oben geschrieben habe, stimmt völlig mit dem Standard überein , während Ihr Argument nur Semantik ist.
Alnitak
5

Ich stimme dem Kommentar von R. zur Verwendung von exit () mit Nachdruck zu, um zu vermeiden, dass der automatische Speicher main()zurückgefordert wird, bevor das Programm tatsächlich endet. Eine return X;Anweisung in main()ist nicht genau gleichbedeutend mit einem Aufruf von exit(X);, da die dynamische Speicherung von main()verschwindet, wenn main()sie zurückgegeben wird. Sie verschwindet jedoch nicht, wenn exit()stattdessen ein Aufruf von erfolgt.

Darüber hinaus weist eine returnAnweisung in C oder einer C-ähnlichen Sprache den Leser stark darauf hin, dass die Ausführung in der aufrufenden Funktion fortgesetzt wird, und während diese Fortsetzung der Ausführung normalerweise technisch wahr ist, wenn Sie die C-Startroutine zählen, die Ihre main()Funktion aufgerufen hat , ist dies nicht der Fall genau das, was du meinst, wenn du den Prozess beenden willst.

Wenn Sie Ihr Programm von einer anderen Funktion aus beenden möchten, müssenmain() Sie aufrufen . Wenn Sie dies auch konsequent tun, wird Ihr Code viel lesbarer und es wird für jeden viel einfacher, Ihren Code neu zu faktorisieren. dh Code kopiert zu einer anderen Funktion wird nicht wegen der zufälligen schlecht benehmen Aussagen , die sollten schon haben Anrufe.exit()main()main()returnexit()

Wenn man all diese Punkte miteinander kombiniert, ist die Schlussfolgerung, dass es zumindest für C eine schlechte Angewohnheit ist , eine returnAnweisung zu verwenden, um das Programm zu beenden main().

Greg A. Woods
quelle
Vielleicht finden Sie 5.1.2.2.3p1 des C-Standards interessant ...
autistisch
Diese Antwort ist für C-Programme eine sorgfältige Prüfung wert, wie in der Antwort kontextuell angegeben. Für die Verwendung mit C ++ muss sorgfältig gegen alle zuvor genannten Einschränkungen abgewogen werden. Für C ++ würde ich empfehlen, exit()generell zu vermeiden , aber verwenden Sie es, wenn a throwoder abort()Alternativen in einem bestimmten Kontext nicht funktionieren. Vermeiden Sie dies jedoch besonders exit()in main und verwenden Sie stattdessen return in main als typische Praxis.
Eljay
5

Tut exit () etwas Besonderes, was 'return' nicht tut?

Bei einigen Compilern für ungewöhnliche Plattformen wird exit()das Argument möglicherweise in den Exit-Wert Ihres Programms übersetzt, während bei einer Rückgabe von main()der Wert möglicherweise ohne Übersetzung direkt an die Host-Umgebung übergeben wird.

Der Standard erfordert in diesen Fällen ein identisches Verhalten (insbesondere heißt es, dass die intRückgabe von etwas, das mit kompatibel main()ist, dem Aufruf exit()mit diesem Wert entsprechen sollte). Das Problem ist, dass verschiedene Betriebssysteme unterschiedliche Konventionen für die Interpretation der Exit-Werte haben. Auf vielen (VIELEN!) Systemen bedeutet 0 Erfolg und alles andere ist ein Fehler. Aber bei VMS bedeuten ungerade Werte Erfolg und gerade Misserfolg. Wenn Sie 0 von zurückgeben main(), wird einem VMS-Benutzer eine böse Nachricht über eine Zugriffsverletzung angezeigt. Es gab eigentlich keine Zugriffsverletzung - das war einfach die Standardnachricht, die dem Fehlercode 0 zugeordnet war.

Dann kam ANSI und segnete EXIT_SUCCESSund EXIT_FAILUREals Argumente, an die man weitergeben konnte exit(). Die Norm sagt auch , dass exit(0)sich verhalten sollten identisch exit(EXIT_SUCCESS), so dass die meisten Implementierungen definieren EXIT_SUCCESSzu 0.

Der Standard daher bringen Sie in einer Bindung auf VMS, wie es läßt keinen einzigen Weg zurückzukehren Ausfall - Code, der den Wert 0 haben passiert.

Der VAX / VMS C-Compiler aus den frühen 1990er Jahren interpretierte daher den Rückgabewert von nicht main(), sondern gab einfach den Wert an die Hostumgebung zurück. Wenn Sie es jedoch verwenden exit()würden, würde es das tun, was der Standard erfordert: Übersetzen EXIT_SUCCESS(oder 0) in einen Erfolgscode und EXIT_FAILUREin einen generischen Fehlercode. Um es zu benutzen EXIT_SUCCESS, musste man es weitergeben exit(), man konnte es nicht zurückgeben main(). Ich weiß nicht, ob modernere Versionen dieses Compilers dieses Verhalten beibehalten haben.

Ein tragbares C-Programm, das früher so aussah:

#include <stdio.h>
#include <stdlib.h>

int main() {
  printf("Hello, World!\n");
  exit(EXIT_SUCCESS);  /* to get good return value to OS */
  /*NOTREACHED*/ /* to silence lint warning */
  return 0;  /* to silence compiler warning */
}

Nebenbei: Wenn ich mich richtig erinnere, ist die VMS-Konvention für Exit-Werte nuancierter als ungerade / gerade. Tatsächlich werden so etwas wie die niedrigen drei Bits verwendet, um einen Schweregrad zu codieren. Im Allgemeinen zeigten die ungeraden Schweregrade jedoch Erfolg oder verschiedene Informationen an, und die geraden zeigten Fehler an.

Adrian McCarthy
quelle
Einige alte Pre-ANSI - Compiler haben könnte den Wert behandelt returneddurch mainunterschiedlich von dem Wert übergeben exit- aber der Standard sagt ausdrücklich : „Wenn der Rückgabetyp der mainFunktion ein Typ kompatibel ist int, eine Rückkehr aus dem ersten Aufruf der mainFunktion entspricht dem Aufruf der exitFunktion mit dem von der mainFunktion als Argument zurückgegebenen Wert ". Das ist C11; C89 / C90 hatte fast den gleichen Wortlaut.
Keith Thompson
Tatsächlich. Einige Compiler aus der ANSI-Ära haben dies jedoch nicht richtig verstanden und erforderten die explizite Verwendung von exit, um den korrekten Rückgabewert an die Hostumgebung zurückzugeben. Da der Standard (auch dann) 0 erfordert als die gleich behandelt werden EXIT_SUCCESS, gab es keine Möglichkeit , eine plattformspezifische zurückzukehren Ausfall - Status mit dem Wert 0, die einige der Compiler der Ära behandeln Rückkehr von der Haupt kann, warum und exit()anders.
Adrian McCarthy
Hast du ein Zitat dafür? Ein separates Problem ist, ob aktuelle Compiler diesen bestimmten Fehler haben. Ihre Antwort ist für die Gegenwart formuliert.
Keith Thompson
Das ist eine faire Kritik. Ich habe den Wortlaut geändert, um den Umfang auf den mir bekannten speziellen Fall zu beschränken.
Adrian McCarthy
0

In C ist das Zurückkehren von maingenau das Gleiche wie das Aufrufen exitmit demselben Wert.

Abschnitt 5.1.2.2.3 der C-Standardzustände :

Wenn der Rückgabetyp der Hauptfunktion ein mit int kompatibler Typ ist, entspricht eine Rückgabe vom ersten Aufruf an die Hauptfunktion dem Aufrufen der Exit-Funktion mit dem von der Hauptfunktion als Argument zurückgegebenen Wert . 11) Wenn Sie das} erreichen, das die Hauptfunktion beendet, wird der Wert 0 zurückgegeben. Wenn der Rückgabetyp nicht mit int kompatibel ist, ist der an die Hostumgebung zurückgegebene Beendigungsstatus nicht angegeben.

Die Regeln für C ++ sind etwas anders, wie in anderen Antworten erwähnt.

dbush
quelle
-1

Es gibt tatsächlich einen Unterschied zwischen exit(0)und return(0)in main- wenn Ihre mainFunktion mehrmals aufgerufen wird.

Das folgende Programm

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

Rennen wie

./program 0 0 0 0

Wird zu folgender Ausgabe führen:

00000

Jedoch dieses:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

Gibt unabhängig von den Argumenten nichts aus.

Wenn Sie sicher sind, dass niemand Sie jemals mainexplizit anrufen wird , ist dies im Allgemeinen technisch gesehen kein großer Unterschied, aber klareren Code beizubehalten, exitwürde viel besser aussehen. Wenn Sie aus irgendeinem Grund anrufen möchten, mainsollten Sie es an Ihre Bedürfnisse anpassen.

Apropos C.

Radrow
quelle