Warum sowohl return als auch exit() in main() funktionieren
Einführung
In der C-Programmierung gibt es zwei Möglichkeiten, ein Programm über die Hauptfunktion zu beenden: mit Return und mit Exit().
int main() { printf("Hello, World!"); return 0; // Method 1: Normal termination } int main() { printf("Hello, World!"); exit(0); // Method 2:Normal termination }
Warum können beide Methoden das Programm korrekt beenden, obwohl sie völlig unterschiedlich aussehen?
In diesem Artikel werden wir dieses Rätsel lösen, indem wir verstehen, wie C-Programme tatsächlich starten und beenden.
Beachten Sie, dass sich dieser Artikel auf die Implementierung in GNU/Linux-Umgebungen konzentriert, insbesondere auf die Verwendung von glibc.
So funktioniert exit()
Lassen Sie uns zunächst untersuchen, wie die Exit-Funktion funktioniert, um den Programmbeendigungsmechanismus zu verstehen.
Die Exit-Funktion ist eine Standardbibliotheksfunktion, die ein Programm ordnungsgemäß beendet.
Intern ist die Funktion _exit, die von Exit aufgerufen wird, in Glibc wie folgt implementiert:
void _exit (int status) { while (1) { INLINE_SYSCALL (exit_group, 1, status); #ifdef ABORT_INSTRUCTION ABORT_INSTRUCTION; #endif } }
Wenn wir uns diese Implementierung ansehen, können wir sehen, dass die _exit-Funktion einen Exit-Status als Argument erhält und exit_group (Systemaufrufnummer 231) aufruft.
Dieser Systemaufruf führt die folgenden Vorgänge aus:
- Sendet eine Programmbeendigungsbenachrichtigung an den Kernel
- Der Kernel führt Bereinigungsvorgänge durch:
- Gibt vom Prozess verwendete Ressourcen frei
- Aktualisiert die Prozesstabelle
- Führt zusätzliche Bereinigungsverfahren durch
Durch diese Vorgänge wird das Programm ordnungsgemäß beendet.
Warum beendet die Rückkehr von main() das Programm auch ordnungsgemäß?
Der versteckte Einstiegspunkt des C-Programms
Um dies zu verstehen, müssen wir eine wichtige Tatsache wissen: C-Programme beginnen eigentlich nicht im Hauptprogramm.
Überprüfen wir die Standardeinstellungen des Linkers (ld), um den tatsächlichen Einstiegspunkt zu sehen:
$ ld --verbose | grep "ENTRY" ENTRY(_start)
Wie diese Ausgabe zeigt, ist der eigentliche Einstiegspunkt eines C-Programms die _start-Funktion. main wird nach _start.
aufgerufen
Die _start-Funktion ist in der Standardbibliothek implementiert und in glibc sieht sie so aus:
_start: # Initialize stack pointer xorl %ebp, %ebp popq %rsi # Get argc movq %rsp, %rdx # Get argv # Setup arguments for main pushq %rsi # Push argc pushq %rdx # Push argv # Call __libc_start_main call __libc_start_main
Die Funktion _start hat zwei Hauptaufgaben:
- Initialisiert den für die Programmausführung erforderlichen Stapelrahmen
- Setzt Befehlszeilenargumente (argc, argv) für die Hauptfunktion ein
Nachdem diese Initialisierungen abgeschlossen sind, wird __libc_start_main aufgerufen.
Diese Funktion ist für den Aufruf der Hauptfunktion verantwortlich.
Lassen Sie uns nun im Detail untersuchen, wie __libc_start_main funktioniert.
Wie __libc_start_main dafür sorgt, dass die Rückgabe funktioniert
__libc_start_call_main, das von __libc_start_main aufgerufen wird, wird wie folgt implementiert:
_Noreturn static void __libc_start_call_main (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv #ifdef LIBC_START_MAIN_AUXVEC_ARG , ElfW(auxv_t) *auxvec #endif ) { int result; /* Memory for the cancellation buffer. */ struct pthread_unwind_buf unwind_buf; int not_first_call; DIAG_PUSH_NEEDS_COMMENT; #if __GNUC_PREREQ (7, 0) /* This call results in a -Wstringop-overflow warning because struct pthread_unwind_buf is smaller than jmp_buf. setjmp and longjmp do not use anything beyond the common prefix (they never access the saved signal mask), so that is a false positive. */ DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overflow="); #endif not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); DIAG_POP_NEEDS_COMMENT; if (__glibc_likely (! not_first_call)) { struct pthread *self = THREAD_SELF; /* Store old info. */ unwind_buf.priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf); unwind_buf.priv.data.cleanup = THREAD_GETMEM (self, cleanup); /* Store the new cleanup handler info. */ THREAD_SETMEM (self, cleanup_jmp_buf, &unwind_buf); /* Run the program. */ result = main (argc, argv, __environ MAIN_AUXVEC_PARAM); } else { /* Remove the thread-local data. */ __nptl_deallocate_tsd (); /* One less thread. Decrement the counter. If it is zero we terminate the entire process. */ result = 0; if (atomic_fetch_add_relaxed (&__nptl_nthreads, -1) != 1) /* Not much left to do but to exit the thread, not the process. */ while (1) INTERNAL_SYSCALL_CALL (exit, 0); } exit (result); }
Bei dieser Implementierung sind die wichtigsten Teile, auf die man sich konzentrieren sollte, wie folgt:
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM); exit(result);
Hier kommt es darauf an, wie die Hauptfunktion ausgeführt wird und wie ihr Rückgabewert behandelt wird:
- Führt die Hauptfunktion aus und speichert ihren Rückgabewert im Ergebnis
- Verwendet den Rückgabewert von main als Argument für Exit
Durch diesen Mechanismus:
- Bei Verwendung von Return in Main → Der Rückgabewert wird an __libc_start_main übergeben, die ihn dann an Exit übergibt
- Wenn exit() direkt in main aufgerufen wird → Das Programm wird sofort beendet
In beiden Fällen wird letztendlich „exit“ aufgerufen, um eine ordnungsgemäße Beendigung des Programms sicherzustellen.
Abschluss
C-Programme verfügen über den folgenden Mechanismus:
- Das Programm beginnt mit _start
- _start bereitet sich auf die Ausführung von main vor
- main wird über __libc_start_main ausgeführt
- Empfangt den Rückgabewert von main und verwendet ihn als Argument für Exit
Durch diesen Mechanismus:
- Auch wenn return in main verwendet wird, wird der Rückgabewert automatisch an Exit übergeben
- Daher beenden sowohl return als auch exit() das Programm ordnungsgemäß
Beachten Sie, dass dieser Mechanismus nicht auf GNU/Linux beschränkt ist; Ähnliche Implementierungen gibt es in anderen Betriebssystemen (wie Windows und macOS) und verschiedenen C-Standardbibliotheken.
Das obige ist der detaillierte Inhalt vonWarum sowohl return als auch exit() in main() funktionieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen











Die Geschichte und Entwicklung von C# und C sind einzigartig, und auch die Zukunftsaussichten sind unterschiedlich. 1.C wurde 1983 von Bjarnestrustrup erfunden, um eine objektorientierte Programmierung in die C-Sprache einzuführen. Sein Evolutionsprozess umfasst mehrere Standardisierungen, z. B. C 11 Einführung von Auto-Keywords und Lambda-Ausdrücken, C 20 Einführung von Konzepten und Coroutinen und sich in Zukunft auf Leistung und Programme auf Systemebene konzentrieren. 2.C# wurde von Microsoft im Jahr 2000 veröffentlicht. Durch die Kombination der Vorteile von C und Java konzentriert sich seine Entwicklung auf Einfachheit und Produktivität. Zum Beispiel führte C#2.0 Generics und C#5.0 ein, die eine asynchrone Programmierung eingeführt haben, die sich in Zukunft auf die Produktivität und das Cloud -Computing der Entwickler konzentrieren.

Es gibt signifikante Unterschiede in den Lernkurven von C# und C- und Entwicklererfahrung. 1) Die Lernkurve von C# ist relativ flach und für rasche Entwicklung und Anwendungen auf Unternehmensebene geeignet. 2) Die Lernkurve von C ist steil und für Steuerszenarien mit hoher Leistung und niedrigem Level geeignet.

Die Anwendung der statischen Analyse in C umfasst hauptsächlich das Erkennen von Problemen mit Speicherverwaltung, das Überprüfen von Code -Logikfehlern und die Verbesserung der Codesicherheit. 1) Statische Analyse kann Probleme wie Speicherlecks, Doppelfreisetzungen und nicht initialisierte Zeiger identifizieren. 2) Es kann ungenutzte Variablen, tote Code und logische Widersprüche erkennen. 3) Statische Analysetools wie die Deckung können Pufferüberlauf, Ganzzahlüberlauf und unsichere API -Aufrufe zur Verbesserung der Codesicherheit erkennen.

C interagiert mit XML über Bibliotheken von Drittanbietern (wie Tinyxml, Pugixml, Xerces-C). 1) Verwenden Sie die Bibliothek, um XML-Dateien zu analysieren und in C-verarbeitbare Datenstrukturen umzuwandeln. 2) Konvertieren Sie beim Generieren von XML die C -Datenstruktur in das XML -Format. 3) In praktischen Anwendungen wird XML häufig für Konfigurationsdateien und Datenaustausch verwendet, um die Entwicklungseffizienz zu verbessern.

Durch die Verwendung der Chrono -Bibliothek in C können Sie Zeit- und Zeitintervalle genauer steuern. Erkunden wir den Charme dieser Bibliothek. Die Chrono -Bibliothek von C ist Teil der Standardbibliothek, die eine moderne Möglichkeit bietet, mit Zeit- und Zeitintervallen umzugehen. Für Programmierer, die in der Zeit gelitten haben.H und CTime, ist Chrono zweifellos ein Segen. Es verbessert nicht nur die Lesbarkeit und Wartbarkeit des Codes, sondern bietet auch eine höhere Genauigkeit und Flexibilität. Beginnen wir mit den Grundlagen. Die Chrono -Bibliothek enthält hauptsächlich die folgenden Schlüsselkomponenten: std :: chrono :: system_clock: repräsentiert die Systemuhr, mit der die aktuelle Zeit erhalten wird. std :: chron

Die Zukunft von C wird sich auf parallele Computer, Sicherheit, Modularisierung und KI/maschinelles Lernen konzentrieren: 1) Paralleles Computer wird durch Merkmale wie Coroutinen verbessert. 2) Die Sicherheit wird durch strengere Mechanismen vom Typ Überprüfung und Speicherverwaltung verbessert. 3) Modulation vereinfacht die Codeorganisation und die Kompilierung. 4) KI und maschinelles Lernen fordern C dazu auf, sich an neue Bedürfnisse anzupassen, wie z. B. numerische Computer- und GPU -Programmierunterstützung.

C# verwendet den automatischen Müllsammlungsmechanismus, während C die manuelle Speicherverwaltung verwendet. Der Müllkollektor von 1. C#verwaltet automatisch den Speicher, um das Risiko eines Speicherlecks zu verringern, kann jedoch zu einer Leistungsverschlechterung führen. 2.C bietet eine flexible Speicherregelung, die für Anwendungen geeignet ist, die eine feine Verwaltung erfordern, aber mit Vorsicht behandelt werden sollten, um Speicherleckage zu vermeiden.
