Was ist der Unterschied zwischen return- und exit-Anweisungen in der C-Programmierung, wenn sie von irgendwo in einem C-Programm aufgerufen werden?
Was ist der Unterschied zwischen Ausgang und Rückkehr?
shona
kriss
- Rückkehr kehrt von der aktuellen Funktion zurück; es ist ein Sprachschlüsselwort wie
for
oderbreak
. - Ausfahrt() beendet das gesamte Programm, egal von wo aus Sie es aufrufen. (Nach dem Spülen der stdio-Puffer usw.).
Der einzige Fall, in dem beide (fast) dasselbe tun, ist in der main()
Funktion, als eine Rückkehr von der Hauptleitung an führt exit()
.
In den meisten C-Implementierungen main
ist eine echte Funktion, die von einem Startcode aufgerufen wird, der so etwas tut int ret = main(argc, argv); exit(ret);
. Der C-Standard garantiert, dass etwas Äquivalentes passiert, wenn main
zurückgibt, jedoch von der Implementierung verarbeitet wird.
Beispiel mit return
:
#include <stdio.h>
void f(){
printf("Executing f\n");
return;
}
int main(){
f();
printf("Back from f\n");
}
Wenn Sie dieses Programm ausführen, druckt es:
Executing f Back from f
Ein weiteres Beispiel für exit()
:
#include <stdio.h>
#include <stdlib.h>
void f(){
printf("Executing f\n");
exit(0);
}
int main(){
f();
printf("Back from f\n");
}
Wenn Sie dieses Programm ausführen, druckt es:
Executing f
Du bekommst nie “Zurück von f”. Beachten Sie auch die #include <stdlib.h>
notwendig, um die Bibliotheksfunktion aufzurufen exit()
.
Beachten Sie auch, dass der Parameter von exit()
ist eine Ganzzahl (es ist der Rückgabestatus des Prozesses, den der Launcher-Prozess erhalten kann; die herkömmliche Verwendung ist 0 für Erfolg oder jeder andere Wert für einen Fehler).
Der Parameter der return-Anweisung ist unabhängig vom Rückgabetyp der Funktion. Wenn die Funktion void zurückgibt, können Sie die Rückgabe am Ende der Funktion weglassen.
Letzter Punkt, exit()
kommen in zwei Geschmacksrichtungen _exit()
und exit()
. Der Unterschied zwischen den Formen ist der exit()
(und Rückkehr von main) ruft mit registrierte Funktionen auf atexit()
oder on_exit()
bevor der Vorgang wirklich beendet wird, während _exit()
(aus #include <unistd.h>
oder sein Synonym _Exit from #include <stdlib.h>
) beendet den Vorgang sofort.
Jetzt gibt es auch Probleme, die spezifisch für C++ sind.
C++ führt viel mehr Arbeit aus als C, wenn es Funktionen verlässt (return
-ing). Insbesondere ruft es Destruktoren von lokalen Objekten auf, die den Gültigkeitsbereich verlassen. In den meisten Fällen kümmern sich Programmierer nicht viel um den Zustand eines Programms, nachdem der Prozess gestoppt wurde, daher würde es keinen großen Unterschied machen: Zugewiesener Speicher wird freigegeben, Dateiressourcen geschlossen und so weiter. Aber es kann wichtig sein, ob Ihr Destruktor IOs ausführt. Zum Beispiel automatisches C++ OStream
lokal erstellte Dateien werden bei einem Aufruf zum Beenden nicht geleert und Sie verlieren möglicherweise einige nicht geleerte Daten (andererseits statische OStream
wird gespült).
Dies wird nicht passieren, wenn Sie das gute alte C verwenden FILE*
Ströme. Diese werden gespült exit()
. Eigentlich ist die Regel die gleiche wie für registrierte Exit-Funktionen, FILE*
wird bei allen normalen Terminierungen gespült, einschließlich exit()
aber nicht Anrufe zu _exit()
oder abbrechen().
Sie sollten auch bedenken, dass C++ einen dritten Weg bietet, um aus einer Funktion herauszukommen: das Auslösen einer Ausnahme. Diese Art, eine Funktion zu verlassen Wille Destruktor aufrufen. Wenn sie nirgendwo in der Aufruferkette abgefangen wird, kann die Ausnahme an die Funktion main() gehen und den Prozess beenden.
Destruktoren statischer C++-Objekte (Globals) werden aufgerufen, wenn Sie eines von beiden aufrufen return
aus main()
oder exit()
überall in Ihrem Programm. Sie werden nicht aufgerufen, wenn das Programm mit beendet wird _exit()
oder abort()
. abort()
ist hauptsächlich im Debug-Modus nützlich, um das Programm sofort zu stoppen und einen Stack-Trace (für Post-Mortem-Analysen) zu erhalten. Es ist normalerweise hinter dem versteckt assert()
Makro nur im Debug-Modus aktiv.
Wann ist exit() nützlich?
exit()
bedeutet, dass Sie den aktuellen Prozess sofort stoppen möchten. Es kann für die Fehlerverwaltung von Nutzen sein, wenn wir auf ein nicht behebbares Problem stoßen, das es Ihrem Code nicht mehr ermöglicht, irgendetwas Nützliches zu tun. Es ist oft praktisch, wenn der Kontrollfluss kompliziert ist und Fehlercodes ganz nach oben weitergegeben werden müssen. Beachten Sie jedoch, dass dies eine schlechte Programmierpraxis ist. Das stillschweigende Beenden des Prozesses ist in den meisten Fällen das schlechtere Verhalten und die eigentliche Fehlerverwaltung sollte bevorzugt werden (oder in C++ mit Ausnahmen).
Direktanrufe an exit()
sind besonders schlecht, wenn sie in Bibliotheken durchgeführt werden, da dies den Bibliotheksbenutzer zum Scheitern bringen wird und es die Wahl des Bibliotheksbenutzers sein sollte, eine Art Fehlerbehebung zu implementieren oder nicht. Wenn Sie ein Beispiel dafür wollen, warum Sie anrufen exit()
aus einer Bibliothek ist schlecht, es führt zum Beispiel dazu, dass Leute diese Frage stellen.
Es gibt eine unbestrittene legitime Verwendung von exit()
als Möglichkeit, einen untergeordneten Prozess zu beenden, der von fork() auf Betriebssystemen gestartet wurde, die dies unterstützen. Es ist normalerweise eine schlechte Idee, vor fork() zum Code zurückzukehren. Dies ist der Grund dafür, warum Funktionen der exec()-Familie niemals an den Aufrufer zurückkehren.
-
exit() ist kein Systemaufruf
– anon
12. August 2010 um 12:25 Uhr
-
Ich benutze normalerweise
return
inmain()
. Natürlich benutze ichreturn 0;
am Ende vonmain()
— Ich benutze manchmalexit();
im Körper der Funktion. Ich mag die C99-Regel über das Herunterfallen am Ende von nichtmain()
gleichwertig seinreturn 0;
Am Ende; es war ein dummer Spezialfall (obwohl C++ den Schaden angerichtet hat).– Jonathan Leffler
19. Januar 2014 um 0:20 Uhr
-
Hinweis: Der C11-Standard hat zusätzliche Funktionen
at_quick_exit()
,_Exit()
(auch in C99) undquick_exit()
. Das_exit()
Die Funktion stammt von POSIX, ist aber im Wesentlichen identisch mit_Exit()
.– Jonathan Leffler
19. Januar 2014 um 1:38 Uhr
-
Wenn Sie ein Beispiel haben wollten, um zu veranschaulichen, warum das Beenden in einer Bibliothek schlecht ist: Es bringt die Leute dazu, diese Frage zu stellen: stackoverflow.com/q/34043652/168175. Und dann vermutlich entweder den Hack verwenden oder sich ohne guten Grund für IPC entscheiden
– Flexo
♦6. September 2016 um 7:29 Uhr
-
@Milan: Einige IO werden gepuffert, dies ist der Fall für OStream. In diesem Zusammenhang bedeutet gespült, dass die Daten tatsächlich an die Konsole gesendet/auf die Festplatte geschrieben und nicht nur im Puffer gehalten wurden. Nicht geleert bedeutet, dass sich die Daten noch in einigen Speicherpuffern in der Anwendung befinden, aber noch nicht an das System gesendet wurden. In C++-Dokumenten nennen sie das zugrunde liegende Systemobjekt, an das Daten gesendet werden, eine “kontrollierte Sequenz”. Es gibt eine explizite Flush-Methode auf OStream, um das zu tun.
– kriss
17. Dezember 2020 um 3:59 Uhr
Ich habe zwei Programme geschrieben:
int main(){return 0;}
und
#include <stdlib.h>
int main(){exit(0)}
Nach der Ausführung gcc -S -O1
. Hier, was ich beim Zusammenbau gefunden habe (nur wichtige Teile):
main:
movl $0, %eax /* setting return value */
ret /* return from main */
und
main:
subq $8, %rsp /* reserving some space */
movl $0, %edi /* setting return value */
call exit /* calling exit function */
/* magic and machine specific wizardry after this call */
Also mein Fazit ist: verwenden return
wann du kannst und exit()
wenn du brauchst.
-
Dies ist eine sehr gute Antwort, abgesehen von der Schlussfolgerung; IMHO motiviert es das Gegenteil: Wir können davon ausgehen, dass die
ret
Die Anweisung kehrt zu einem Punkt zurück, an dem möglicherweise zusätzliche Arbeit geleistet wird, aber am Ende wird die Funktion exit() trotzdem aufgerufen – oder wie könnte vermieden werden, das zu tun, was exit() tut? Das bedeutet, dass ersteres nur einige zusätzliche “duplizierte” Arbeiten ohne Vorteil gegenüber der zweiten Lösung erledigt, da exit() sowieso ganz am Ende aufgerufen wird.– max
18. März 2019 um 19:45 Uhr
-
@Max: Es ist nicht verboten, main rekursiv aus Ihrem eigenen Programm aufzurufen. Von nun an sollten Sie nicht davon ausgehen, dass die Rückkehr von main sofort beendet wird, dies würde die Codesemantik brechen. In einigen (seltenen) Fällen ist es sogar nützlich. Zum Beispiel, um etwas Kontext vorzubereiten/zu löschen, bevor main aufgerufen wird, nachdem der Code-Einstiegspunkt auf etwas anderes als main geändert wurde. Dies könnte sogar durch eine Code-Injection-Methode zur Laufzeit erfolgen. Natürlich, wenn es Ihr Programm ist, tun Sie, was Sie bevorzugen oder glauben, dass es einfacher zu lesen ist.
– kriss
3. April 2019 um 9:13 Uhr
-
@kriss: Dies ist ein ausgezeichneter Punkt, der zuvor noch nicht erwähnt wurde. Obwohl ich denke, dass es äußerst selten vorkommt, dass main() rekursiv aufgerufen wird, verdeutlicht diese Möglichkeit IMHO besser als alles andere den Unterschied zwischen return und exit(0).
– max
11. April 2019 um 0:35 Uhr
Dummkopf
In C gibt es keinen großen Unterschied, wenn es in der Startfunktion des Programms verwendet wird (was sein kann main()
, wmain()
, _tmain()
oder der von Ihrem Compiler verwendete Standardname).
Wenn du return
in main()
die Kontrolle geht zurück an die _start()
Funktion in der C-Bibliothek, die ursprünglich Ihr Programm gestartet hat, die dann aufruft exit()
Sowieso. Es ist also eigentlich egal, welchen du verwendest.
-
Es ist wichtig. exit() beendet das Programm sofort, egal wo es aufgerufen wird. return beendet nur die aktuelle Funktion. Der einzige Ort, an dem sie dasselbe tun, ist in main ()
– Benutzer47589
11. August 2010 um 23:17 Uhr
-
Danke, ich habe den Wortlaut korrigiert. Es ist nicht unbedingt nur in main(), da nicht alle Compiler denselben Funktionsnamen für die Startup-Funktion verwenden.
– Dummkopf
11. August 2010 um 23:23 Uhr
-
Ich schätze, Sie schreiben alle Ihre Programme in einer großen Hauptfunktion? 😉
– CJohnson
12. August 2010 um 10:41 Uhr
-
Die Antwort ist nicht vollständig, aber dennoch informativ.
– Jagdisch
26. Juni 2015 um 2:56 Uhr
In den meisten Fällen gibt es in einem C-Programm keinen Unterschied zwischen der Verwendung von return
und anrufen exit()
zu beenden main()
.
Der Zeitpunkt, an dem es einen Unterschied gibt, ist, wenn Sie Code erstellt haben, der ausgeführt wird, nachdem Sie von zurückgekehrt sind main()
das auf lokalen Variablen beruht main()
. Eine Möglichkeit, die sich manifestiert, ist mit setvbuf()
:
int main(void)
{
char buffer[BUFSIZ];
setvbuf(stdout, buffer, _IOFBF, BUFSIZ);
…code using stdout…
return 0;
}
In diesem Beispiel wird der Puffer über bereitgestellt setvbuf()
geht aus dem Geltungsbereich, wenn main()
zurück, aber der Code, der spült und schließt stdout
wird versuchen, diesen Puffer zu verwenden. Dies führt zu undefiniertem Verhalten.
Der andere Mechanismus ist das Aufrufen atexit()
mit einer Funktion, die auf Daten von zugreift main()
— über einen Zeiger. Dies ist schwieriger einzurichten als die Funktionen, die über die aufgerufen werden atexit()
Mechanismus werden keine Argumente angegeben. Sie müssen also so etwas tun:
static void *at_exit_data = 0;
static void at_exit_handler(void)
{
char *str = at_exit_data;
printf("Exiting: %s\n", str);
}
int main(void);
{
char buffer[] = "Message to be printed via functions registered with at_exit()";
at_exit_data = buffer;
at_exit(at_exit_handler);
…processing…
return 0;
}
Wieder zeigte der Puffer auf by at_exit_data
nicht mehr existiert, wenn das Programm zurückkehrt main()
und so ruft die Handler-Funktion undefiniertes Verhalten auf.
Es gibt eine verwandte Funktion, at_quick_exit()
aber die damit registrierten Funktionen werden nur aufgerufen, wenn die quick_exit()
Funktion aufgerufen wird, was verhindert, dass die Funktionen danach aufgerufen werden main()
kehrt zurück.
die return-Anweisung verlässt die aktuelle Funktion und exit() verlässt das Programm
they are the same when used in main() function
Auch return ist eine Anweisung, während exit() eine Funktion ist, die die Header-Datei stdlb.h erfordert
-
Sie sind gleich wenn main wird von einer der Funktionen aufgerufen in Ihr Programm. Das ist selten; Die meisten Programme verwenden kein rekursives oder wiedereintretendes main, aber wenn wir über Sprachdetails sprechen, dann ist das eine Möglichkeit.
– Peter Cordes
12. Juni 2021 um 15:23 Uhr
-
Sie sind gleich wenn main wird von einer der Funktionen aufgerufen in Ihr Programm. Das ist selten; Die meisten Programme verwenden kein rekursives oder wiedereintretendes main, aber wenn wir über Sprachdetails sprechen, dann ist das eine Möglichkeit.
– Peter Cordes
12. Juni 2021 um 15:23 Uhr
Ich habe das „Als Duplikat schließen“ entfernt, da das ausgewählte Duplikat sowohl mit C als auch mit C++ gekennzeichnet ist und es nicht nötig ist, die Leute mit den C++-Problemen zu verwirren (die sich von den C-Problemen unterscheiden, obwohl sie ihnen vage ähnlich sind). Das Duplikat war return-Anweisung vs exit() in main()?
– Jonathan Leffler
20. April um 3:09 Uhr