Was sollte main () in C und C ++ zurückgeben?


694

28
Ich denke immer noch, dass es auch ziemlich vage ist. Definieren Sie für mich "am effizientesten". In welchem ​​Sinne effizient? Im Sinne von weniger Speicherplatz? Im Sinne von schneller laufen? Ich kann die nützlichen Antworten sehen, aber ich denke immer noch, dass die Frage ziemlich schlecht formuliert ist.
Onorio Catenacci

7
Pish nobel, der Kontext von effizient ist hier offensichtlich, insbesondere bei den Beispielen (die dort wahrscheinlich die Definition von "effizient" verdeutlichen). Hoffentlich kroch der arme Puffer nicht in ein Loch und bereute die Frage völlig. Man könnte sagen, dass unabhängig von void oder int ein Wert zurückgegeben wird, sodass er keinen Einfluss auf die Dateigröße, die ausgeführten Operationen oder den zugewiesenen Speicher hat. Und Menschen geben in den meisten Betriebssystemen bei Erfolg 0 und bei Erfolg oder Misserfolg etwas anderes zurück, aber es gibt keinen Standard. Letztendlich kein Unterschied in der Effizienz in irgendeiner offensichtlichen Weise.
Kit10

"richtig (am effizientesten)" macht keinen Sinn. Effizient ist eine Sache, richtig ist eine andere. mainwird einmal aufgerufen (und kann in C ++ nur einmal aufgerufen werden: keine Rekursion). Wenn Sie nicht möchten, dass die Ausführung viel Zeit in mainAnspruch nimmt, rufen Sie das Programm nicht häufig auf: Lassen Sie das Programm die Wiederholung implementieren.
Kaz

2
Ich finde es interessant, dass keine der Antworten, soweit ich das beurteilen kann, ein voll funktionsfähiges Beispiel liefert, einschließlich der #includeAussagen
puk

3
Rückgabewerte sind auf einer Plattform ohne Betriebssystem nicht sinnvoll. Du kehrst zu nichts zurück. Wenn Sie einen Hit returnin main(...)auf einem Embedded - Gerät, geht das System in einen unvorhersehbaren Zustand und die Waschmaschine wird sich selbst bewusst und versucht , dich zu töten. Also verwenden wir void main()in diesem Fall. Dies ist eine branchenübliche Praxis für Bare-Metal-Embedded.
3Dave

Antworten:


569

Der Rückgabewert für maingibt an, wie das Programm beendet wurde. Der normale Ausgang wird durch einen Rückgabewert von 0 von dargestellt main. Ein abnormaler Ausgang wird durch eine Rückkehr ungleich Null signalisiert, es gibt jedoch keinen Standard für die Interpretation von Codes ungleich Null. Wie von anderen angemerkt, void main()ist dies nach dem C ++ - Standard verboten und sollte nicht verwendet werden. Die gültigen C ++ - mainSignaturen sind:

int main()

und

int main(int argc, char* argv[])

das ist äquivalent zu

int main(int argc, char** argv)

Es ist auch erwähnenswert, dass in C ++ int main()ohne return-Anweisung belassen werden kann. An diesem Punkt wird standardmäßig 0 zurückgegeben. Dies gilt auch für ein C99-Programm. Ob return 0;weggelassen werden soll oder nicht, steht zur Debatte. Der Bereich der gültigen Hauptsignaturen des C-Programms ist viel größer.

Effizienz ist kein Problem mit der mainFunktion. Es kann gemäß dem C ++ - Standard nur einmal eingegeben und verlassen werden (Markieren des Starts und der Beendigung des Programms). Für C ist eine erneute Eingabe main()zulässig, sollte jedoch vermieden werden.


69
main kann mehrmals eingegeben / verlassen werden, aber dieses Programm würde wahrscheinlich keine Designpreise gewinnen;)
korona

13
C99 hat auch die C ++ - Fehlfunktion, dass das Erreichen des Endes der Funktion main () der Rückgabe von 0 entspricht - wenn main () so definiert ist, dass ein mit int kompatibler Typ zurückgegeben wird (Abschnitt 5.1.2.2.3).
Jonathan Leffler

62
Die erneute Eingabe von main ist in C ++ nicht gültig. Ausdrücklich in der Norm heißt es in 3.6.1.3: "main darf nicht innerhalb eines Programms verwendet werden"
workmad3,

117
stdlib.h bietet EXIT_SUCCESS und EXIT_FAILURE für diesen Zweck
Clay

20
0 und ungleich Null sind korrekt, aber für jemanden, der Ihren Code liest, völlig bedeutungslos. Diese Frage ist ein Beweis dafür, dass die Leute nicht wissen, was gültige / ungültige Codes sind. EXIT_SUCCESS / EXIT_FAILURE sind viel klarer.
JaredPar

169

Die akzeptierte Antwort scheint auf C ++ ausgerichtet zu sein, daher dachte ich, ich würde eine Antwort hinzufügen, die sich auf C bezieht, und dies unterscheidet sich in einigen Punkten.

ISO / IEC 9899: 1989 (C90):

main() sollte wie folgt deklariert werden:

int main(void)
int main(int argc, char **argv)

Oder gleichwertig. Zum Beispiel int main(int argc, char *argv[])entspricht dem zweiten. Außerdem kann der intRückgabetyp weggelassen werden, da dies eine Standardeinstellung ist.

Wenn eine Implementierung dies zulässt, main()kann dies auf andere Weise deklariert werden. Dadurch wird die Programmimplementierung jedoch definiert und ist nicht mehr streng konform.

Der Standard definiert 3 Werte für die Rückgabe, die streng konform sind (dh nicht auf dem durch die Implementierung definierten Verhalten beruhen): 0und EXIT_SUCCESSfür eine erfolgreiche Beendigung und EXIT_FAILUREfür eine nicht erfolgreiche Beendigung. Alle anderen Werte sind nicht standardisiert und die Implementierung definiert. main()muss returnam Ende eine explizite Anweisung enthalten, um undefiniertes Verhalten zu vermeiden.

Schließlich ist aus Sicht der Standards nichts Falsches daran, main()von einem Programm aus aufzurufen .

ISO / IEC 9899: 1999 (C99):

Für C99 ist alles wie oben, außer:

  • Der intRückgabetyp darf nicht weggelassen werden.
  • Sie können die return-Anweisung von weglassen main(). Wenn Sie dies tun und main()fertig sind, gibt es eine implizite return 0.

1
@Lundin Ich glaube nicht, dass Sie ein Zitat benötigen, um zu sagen, dass jemand einen Compiler erstellen darf, der nicht standardkonforme Programme akzeptiert, oder einen nicht standardkonformen Compiler haben darf. Das ist allgemein bekannt und
gesunder

4
@KABoissonneault Implementierungsdefiniertes Verhalten ist ein Begriff aus dem Standard, im Gegensatz zu vollständig undokumentiertem Verhalten. Wenn Sie etwas implementieren, das als implementierungsdefiniertes Verhalten aufgeführt ist, folgen Sie weiterhin dem Standard. In diesem zitierten Fall listet C89 kein solches implementierungsdefiniertes Verhalten auf, daher die Notwendigkeit eines Zitats, um zu beweisen, dass er Dinge nicht einfach aus heiterem Himmel erfunden hat.
Lundin

1
@Lundin Du siehst das falsch. Es handelt sich nicht um ein implementierungsdefiniertes Verhalten, sondern um eine Implementierung, die vom Standard abweicht, wenn sie dies wünscht. Es ist eher so, als würde ein Kind seinen Eltern nicht gehorchen: Sie brauchen kein Zitat der Eltern, um zu sagen, wie ein Kind gegen das verstoßen kann, was die Eltern gesagt haben. Sie wissen nur, dass das Kind in dem Moment, in dem es sich dafür entscheidet, nicht mehr den Gildenlinien seiner Eltern entspricht
KABoissonneault

2
@KABoissonneault Der Teil, den ich in meinem Kommentar zitiert habe, befasst sich definitiv mit implementierungsdefiniertem Verhalten (im Gegensatz zu nicht standardmäßigen Compiler-Erweiterungen ). Daher spreche ich über implementierungsdefiniertes Verhalten. Wenn Sie einen Monolog über etwas anderes haben, viel Glück damit.
Lundin

1
@Lundin Ich denke, der Wortlaut im Zitat ist verwirrend (der Teil, in dem sie sagen "aber das macht die Programmimplementierung definiert"), aber ich bin mir ziemlich sicher, dass die Person über nicht standardmäßiges Verhalten gesprochen hat (wie in "Wenn eine Implementierung gesagt wird) erlaubt es "und" und nicht mehr strikt konform [zum Standard] ") im Gegensatz zum tatsächlich implementierungsdefinierten Verhalten. Die Person sollte ihre Antwort definitiv umformulieren, aber ich denke immer noch nicht, dass ein Zitat aus dem Standard dazu notwendig ist
KABoissonneault

117

Standard C - Hosted Environment

Für eine gehostete Umgebung (das ist die normale) lautet der C11-Standard (ISO / IEC 9899: 2011):

5.1.2.2.1 Programmstart

Die beim Programmstart aufgerufene Funktion wird benannt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von intund ohne Parameter definiert werden:

int main(void) { /* ... */ }

oder mit zwei Parametern (hier als argcund bezeichnet argv, obwohl beliebige Namen verwendet werden können, da sie lokal für die Funktion sind, in der sie deklariert sind):

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

oder gleichwertig; 10) oder auf eine andere implementierungsdefinierte Weise.

Wenn sie deklariert sind, müssen die Parameter der Hauptfunktion die folgenden Einschränkungen erfüllen:

  • Der Wert von argcist nicht negativ.
  • argv[argc] soll ein Nullzeiger sein.
  • Wenn der Wert von argcgrößer als Null ist, müssen die Array-Mitglieder argv[0]bis argv[argc-1]einschließlich Zeiger auf Zeichenfolgen enthalten, die vor dem Programmstart von der Host-Umgebung implementierungsdefinierte Werte erhalten. Die Absicht besteht darin, dem Programm Informationen zu liefern, die vor dem Programmstart von einer anderen Stelle in der gehosteten Umgebung ermittelt wurden. Wenn die Host-Umgebung nicht in der Lage ist, Zeichenfolgen mit Buchstaben in Groß- und Kleinbuchstaben zu versehen, muss die Implementierung sicherstellen, dass die Zeichenfolgen in Kleinbuchstaben empfangen werden.
  • Wenn der Wert von argcgrößer als Null ist, argv[0] repräsentiert die Zeichenfolge, auf die durch zeigt , den Programmnamen. argv[0][0]muss das Nullzeichen sein, wenn der Programmname in der Hostumgebung nicht verfügbar ist. Wenn der Wert von argcgrößer als eins ist, repräsentieren die Zeichenfolgen, auf die durch gezeigt argv[1]wird argv[argc-1], die Programmparameter.
  • Die Parameter argcund argvund die Zeichenfolgen, auf die das argvArray zeigt, müssen vom Programm geändert werden können und ihre zuletzt gespeicherten Werte zwischen Programmstart und Programmbeendigung beibehalten.

10) Somit intkann durch einen Typedef-Namen ersetzt werden, der als definiert ist int, oder der Typ von argvkann geschrieben werden als char **argvund so weiter.

Programmbeendigung in C99 oder C11

Der von zurückgegebene Wert main()wird implementierungsdefiniert an die 'Umgebung' übertragen.

5.1.2.2.3 Programmbeendigung

1 Wenn der Rückgabetyp der mainFunktion mit einem kompatiblen Typ kompatibel ist int, entspricht eine Rückgabe vom ersten Aufruf der mainFunktion dem Aufrufen der exitFunktion mit dem von der mainFunktion als Argument zurückgegebenen Wert . 11) Wenn Sie das erreichen, das }die mainFunktion beendet, wird der Wert 0 zurückgegeben. Wenn der Rückgabetyp nicht kompatibel ist int, ist der an die Hostumgebung zurückgegebene Beendigungsstatus nicht angegeben.

11) Gemäß 6.2.4 ist die Lebensdauer von Objekten mit der angegebenen automatischen Lagerdauer main im ersteren Fall abgelaufen, auch wenn sie im letzteren Fall nicht vorhanden wären.

Beachten Sie, dass dies 0als "Erfolg" vorgeschrieben ist. Sie können EXIT_FAILUREund EXIT_SUCCESSvon verwenden, <stdlib.h>wenn Sie es vorziehen, aber 0 ist gut etabliert, ebenso wie 1. Siehe auch Exit-Codes größer als 255 - möglich? .

In C89 (und damit in Microsoft C) gibt es keine Aussage darüber, was passiert, wenn die main()Funktion zurückgibt, aber keinen Rückgabewert angibt. es führt daher zu undefiniertem Verhalten.

7.22.4.4 Die exitFunktion

¶5 Schließlich wird die Steuerung an die Hostumgebung zurückgegeben. Wenn der Wert von statusNull ist oder EXIT_SUCCESS, wird eine implementierungsdefinierte Form des Status erfolgreiche Beendigung zurückgegeben. Wenn der Wert von statusist EXIT_FAILURE, wird eine implementierungsdefinierte Form des Status " Nicht erfolgreiche Beendigung" zurückgegeben. Andernfalls ist der zurückgegebene Status implementierungsdefiniert.

Standard C ++ - Gehostete Umgebung

Der C ++ 11-Standard (ISO / IEC 14882: 2011) lautet:

3.6.1 Hauptfunktion [basic.start.main]

¶1 Ein Programm muss eine globale Funktion namens main enthalten, die den festgelegten Start des Programms darstellt. [...]

¶2 Eine Implementierung darf die Hauptfunktion nicht vordefinieren. Diese Funktion darf nicht überlastet werden. Es muss einen Rückgabetyp vom Typ int haben, ansonsten ist sein Typ implementierungsdefiniert. Alle Implementierungen müssen beide der folgenden Definitionen von main zulassen:

int main() { /* ... */ }

und

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

In letzterer Form argcist die Anzahl der Argumente anzugeben, die aus der Umgebung, in der das Programm ausgeführt wird, an das Programm übergeben werden. Wenn argcungleich null ist sind diese Argumente geliefert werden argv[0] durch argv[argc-1]den Anfangsbuchstaben von nullterminierten Multibyte Strings (NTMBSs) (17.5.2.1.4.2) als Zeiger und argv[0]wird der Zeiger auf den ursprünglichen Charakter eines NTMBS sein, der den Namen verwendet wird, stellt an Rufen Sie das Programm auf oder "". Der Wert von argcdarf nicht negativ sein. Der Wert von argv[argc] muss 0 sein. [Hinweis: Es wird empfohlen, danach weitere (optionale) Parameter hinzuzufügen argv. - Endnote]

¶3 Die Funktion maindarf nicht innerhalb eines Programms verwendet werden. Die Verknüpfung (3.5) von mainist implementierungsdefiniert. [...]

¶5 Eine return-Anweisung in main bewirkt, dass die main-Funktion verlassen wird (Objekte mit automatischer Speicherdauer werden zerstört) und std::exitder return-Wert als Argument aufgerufen wird . Wenn die Steuerung das Ende von main erreicht, ohne auf eine return-Anweisung zu stoßen, bewirkt dies die Ausführung

return 0;

Der C ++ - Standard sagt ausdrücklich "Es [die Hauptfunktion] soll einen Rückgabetyp haben int, aber ansonsten ist sein Typ implementierungsdefiniert" und erfordert, dass dieselben zwei Signaturen wie der C-Standard als Optionen unterstützt werden. Ein 'void main ()' ist vom C ++ - Standard direkt nicht zulässig, obwohl es nichts tun kann, um eine nicht standardmäßige Implementierung zu stoppen, die Alternativen zulässt. Beachten Sie, dass C ++ dem Benutzer das Aufrufen verbietet main(der C-Standard jedoch nicht).

Es gibt einen Absatz von §18.5 Start und Beendigung im C ++ 11-Standard, der mit dem Absatz aus §7.22.4.4 identisch ist. Die exitFunktion im C11-Standard (oben zitiert), abgesehen von einer Fußnote (die dies einfach dokumentiert EXIT_SUCCESSund EXIT_FAILUREdefiniert ist in <cstdlib>).

Standard C - Gemeinsame Erweiterung

Klassischerweise unterstützen Unix-Systeme eine dritte Variante:

int main(int argc, char **argv, char **envp) { ... }

Das dritte Argument ist eine nullterminierte Liste von Zeigern auf Zeichenfolgen, von denen jede eine Umgebungsvariable ist, die einen Namen, ein Gleichheitszeichen und einen Wert (möglicherweise leer) hat. Wenn Sie dies nicht verwenden, können Sie trotzdem über ' extern char **environ;' in die Umgebung gelangen . Diese globale Variable ist unter denen in POSIX insofern einzigartig, als sie keinen Header hat, der sie deklariert.

Dies wird von der C-Norm als gemeinsame Erweiterung anerkannt, die in Anhang J dokumentiert ist:

J.5.1 Umgebungsargumente

¶1 In einer gehosteten Umgebung erhält die Hauptfunktion ein drittes Argument, char *envp[]das auf ein nullterminiertes Array von Zeigern charverweist, von denen jeder auf eine Zeichenfolge verweist, die Informationen über die Umgebung für diese Ausführung des Programms bereitstellt (5.1). 2.2.1).

Microsoft C.

Der Microsoft VS 2010- Compiler ist interessant. Die Website sagt:

Die Deklarationssyntax für main lautet

 int main();

oder optional

int main(int argc, char *argv[], char *envp[]);

Alternativ können die Funktionen mainund wmainals voidreturn deklariert werden (kein Rückgabewert). Wenn Sie void mainoder wmainals ungültig deklarieren , können Sie keinen Exit-Code mithilfe einer return-Anweisung an den übergeordneten Prozess oder das übergeordnete Betriebssystem zurückgeben. Um einen Exit-Code zurückzugeben, wenn mainoder wmainals deklariert ist void, müssen Sie die exitFunktion verwenden.

Mir ist nicht klar, was passiert (welcher Exit-Code wird an das übergeordnete oder Betriebssystem zurückgegeben), wenn ein Programm mit beendet void main()wird - und die MS-Website ist ebenfalls still.

Interessanterweise schreibt MS nicht die Version mit zwei Argumenten vor main(), die die C- und C ++ - Standards erfordern. Es wird nur eine Form mit drei Argumenten vorgeschrieben, wobei das dritte Argument char **envpein Zeiger auf eine Liste von Umgebungsvariablen ist.

Auf der Microsoft-Seite werden auch einige andere Alternativen aufgeführt, für wmain()die breite Zeichenfolgen erforderlich sind, und einige weitere.

Die Microsoft Visual Studio 2005- Version dieser Seite wird nicht void main()als Alternative aufgeführt. Die Versionen ab Microsoft Visual Studio 2008 tun dies.

Standard C - Freistehende Umgebung

Wie bereits erwähnt, gelten die oben genannten Anforderungen für gehostete Umgebungen. Wenn Sie mit einer freistehenden Umgebung arbeiten (die Alternative zu einer gehosteten Umgebung), hat der Standard viel weniger zu sagen. In einer freistehenden Umgebung muss die beim Programmstart aufgerufene Funktion nicht aufgerufen werden, mainund der Rückgabetyp unterliegt keinen Einschränkungen. Der Standard sagt:

5.1.2 Ausführungsumgebungen

Es werden zwei Ausführungsumgebungen definiert: freistehend und gehostet. In beiden Fällen erfolgt der Programmstart, wenn eine bestimmte C-Funktion von der Ausführungsumgebung aufgerufen wird. Alle Objekte mit statischer Speicherdauer müssen vor dem Programmstart initialisiert (auf ihre Anfangswerte gesetzt) ​​werden. Die Art und der Zeitpunkt einer solchen Initialisierung sind ansonsten nicht spezifiziert. Die Programmbeendigung gibt die Kontrolle an die Ausführungsumgebung zurück.

5.1.2.1 Freistehende Umgebung

In einer freistehenden Umgebung (in der die Ausführung des C-Programms ohne Nutzen eines Betriebssystems erfolgen kann) sind Name und Typ der beim Programmstart aufgerufenen Funktion implementierungsdefiniert. Alle Bibliothekseinrichtungen, die einem freistehenden Programm zur Verfügung stehen, mit Ausnahme der in Abschnitt 4 geforderten Mindestmenge, sind implementierungsdefiniert.

Der Effekt der Programmbeendigung in einer freistehenden Umgebung ist implementierungsdefiniert.

Der Querverweis auf Klausel 4 Konformität bezieht sich auf Folgendes:

¶5 Ein streng konformes Programm darf nur die Merkmale der in dieser Internationalen Norm angegebenen Sprache und Bibliothek verwenden. 3) Es darf keine Ausgabe erzeugen, die von einem nicht spezifizierten, undefinierten oder implementierungsdefinierten Verhalten abhängt, und darf keine Mindestimplementierungsgrenze überschreiten.

¶6 Die beiden Formen der konformen Implementierung sind gehostet und freistehend . Eine konform gehostete Implementierung akzeptiert streng konforme Programme. Eine konforme freistehende Ausführung ist jedes streng konformes Programm akzeptieren , in dem die Nutzung der Funktionen in der Bibliothek Klausel (Ziffer 7) spezifizierten den Inhalt der Standard - Header eingeschlossen ist <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, und <stdnoreturn.h>. Eine konforme Implementierung kann Erweiterungen (einschließlich zusätzlicher Bibliotheksfunktionen) aufweisen, sofern sie das Verhalten eines streng konformen Programms nicht ändern. 4)

¶7 Ein konformes Programm ist für eine konforme Implementierung akzeptabel. 5)

3) Ein streng konformes Programm kann bedingte Funktionen verwenden (siehe 6.10.8.3), vorausgesetzt, die Verwendung wird durch eine geeignete Vorverarbeitungsrichtlinie für bedingte Einbeziehung unter Verwendung des zugehörigen Makros geschützt. Zum Beispiel:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */
    /* ... */
    fesetround(FE_UPWARD);
    /* ... */
#endif

4) Dies bedeutet, dass eine konforme Implementierung keine anderen Kennungen als die in dieser Internationalen Norm ausdrücklich reservierten reserviert.

5) Streng konforme Programme sollen unter konformen Implementierungen maximal portierbar sein. Konforme Programme können von nicht portierbaren Funktionen einer konformen Implementierung abhängen.

Es fällt auf, dass der einzige Header, der für eine freistehende Umgebung erforderlich ist, die tatsächlich Funktionen definiert, <stdarg.h>(und selbst diese können und sind oft nur Makros) sind.

Standard C ++ - Freistehende Umgebung

So wie der C-Standard sowohl gehostete als auch freistehende Umgebungen erkennt, erkennt auch der C ++ - Standard dies. (Zitate aus ISO / IEC 14882: 2011.)

1.4 Implementierungskonformität [intro.compliance]

¶7 Es werden zwei Arten von Implementierungen definiert: eine gehostete Implementierung und eine freistehende Implementierung . Für eine gehostete Implementierung definiert dieser Internationale Standard den Satz verfügbarer Bibliotheken. Eine freistehende Implementierung ist eine Implementierung, bei der die Ausführung ohne den Vorteil eines Betriebssystems erfolgen kann, und verfügt über einen implementierungsdefinierten Satz von Bibliotheken, der bestimmte Sprachunterstützungsbibliotheken enthält (17.6.1.3).

¶8 Eine konforme Implementierung kann Erweiterungen (einschließlich zusätzlicher Bibliotheksfunktionen) aufweisen, sofern sie das Verhalten eines wohlgeformten Programms nicht ändern. Implementierungen sind erforderlich, um Programme zu diagnostizieren, die solche Erweiterungen verwenden, die gemäß dieser internationalen Norm schlecht ausgebildet sind. Danach können sie jedoch solche Programme kompilieren und ausführen.

¶9 Jede Implementierung muss eine Dokumentation enthalten, die alle bedingt unterstützten Konstrukte identifiziert, die nicht unterstützt werden, und alle länderspezifischen Merkmale definiert. 3

3) Diese Dokumentation definiert auch das implementierungsdefinierte Verhalten. siehe 1.9.

17.6.1.3 Freistehende Implementierungen [Compliance]

Es werden zwei Arten von Implementierungen definiert: gehostet und freistehend (1.4). Für eine gehostete Implementierung beschreibt dieser Internationale Standard den Satz verfügbarer Header.

Eine freistehende Implementierung verfügt über einen implementierungsdefinierten Satz von Headern. Dieser Satz muss mindestens die in Tabelle 16 aufgeführten Überschriften enthalten.

Die mitgelieferte Version des Headers <cstdlib>muss mindestens die Funktionen erklären abort, atexit, at_quick_exit, exit, und quick_exit(18,5). Die anderen in dieser Tabelle aufgeführten Header müssen dieselben Anforderungen erfüllen wie für eine gehostete Implementierung.

Tabelle 16 - C ++ - Header für freistehende Implementierungen

Subclause                           Header(s)
                                    <ciso646>
18.2  Types                         <cstddef>
18.3  Implementation properties     <cfloat> <limits> <climits>
18.4  Integer types                 <cstdint>
18.5  Start and termination         <cstdlib>
18.6  Dynamic memory management     <new>
18.7  Type identification           <typeinfo>
18.8  Exception handling            <exception>
18.9  Initializer lists             <initializer_list>
18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>
20.9  Type traits                   <type_traits>
29    Atomics                       <atomic>

Was ist mit der Verwendung int main()in C?

Die Norm §5.1.2.2.1 der Norm C11 zeigt die bevorzugte Notation -  int main(void)- aber es gibt auch zwei Beispiele in der Norm, die zeigen int main(): §6.5.3.4 ¶8 und §6.7.6.3 ¶20 . Nun ist es wichtig anzumerken, dass Beispiele nicht „normativ“ sind. Sie sind nur zur Veranschaulichung. Wenn die Beispiele Fehler enthalten, wirken sie sich nicht direkt auf den Haupttext des Standards aus. Das heißt, sie weisen stark auf das erwartete Verhalten hin. Wenn der Standard also int main()ein Beispiel enthält, deutet dies darauf hin, dass dies int main()nicht verboten ist, auch wenn es nicht die bevorzugte Notation ist.

6.5.3.4 Die sizeofund _AlignofOperatoren

¶8 BEISPIEL 3 In diesem Beispiel wird die Größe eines Arrays variabler Länge berechnet und von einer Funktion zurückgegeben:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}

@DavidBowling: Eine Funktionsdefinition wie int main(){ … }gibt an, dass die Funktion keine Argumente akzeptiert, aber keinen Funktionsprototyp AFAICT bereitstellt. Denn main()das ist selten ein Problem; Dies bedeutet, dass bei rekursiven Aufrufen von main()die Argumente nicht überprüft werden. Bei anderen Funktionen ist dies eher ein Problem. Wenn die Funktion aufgerufen wird, benötigen Sie wirklich einen Prototyp im Gültigkeitsbereich, um sicherzustellen, dass die Argumente korrekt sind.
Jonathan Leffler

1
@DavidBowling: Sie rufen normalerweise nicht main()rekursiv außerhalb von Orten wie IOCCC an. Ich habe ein Testprogramm, das dies tut - hauptsächlich aus Gründen der Neuheit. Wenn Sie int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }GCC haben und mit GCC kompilieren und nicht einschließen -Wstrict-prototypes, wird es unter strengen Warnungen sauber kompiliert. Wenn dies der main(void)Fall ist , kann es nicht kompiliert werden.
Jonathan Leffler

61

Ich glaube das main()sollte entweder EXIT_SUCCESSoder zurückkehren EXIT_FAILURE. Sie sind definiert instdlib.h


20
0 ist auch Standard.
Chris Young

2
@ChrisYoung Es gibt EXIT_SUCCESSund EXIT_FAILUREweil einige historische Betriebssysteme (VMS?) Eine andere Zahl als 0 verwendeten, um den Erfolg anzuzeigen . Es ist heutzutage überall 0.
Fuz

5
@FUZxxl Sie haben Recht, aber das steht nicht im Widerspruch zu meinem Kommentar. EXIT_SUCCESS kann zwar ungleich Null sein, aber die Standards (C89, C99, C11) definieren alle 0 (sowie EXIT_SUCCESS) als eine implementierungsdefinierte Form der erfolgreichen Beendigung des Status.
Chris Young

2
@FUZxxl: Es ist wahr, dass das VMS ungerade Werte (wie 1) verwendet hat, um Erfolg anzuzeigen, und gerade Werte (wie 0), um Fehler anzuzeigen. Leider wurde der ursprüngliche ANSI C-Standard so interpretiert, dass EXIT_SUCCESS 0 sein musste, sodass die Rückgabe von EXIT_SUCCESS von main genau das falsche Verhalten in VMS ergab. Das tragbare, was für VMS zu tun war, war die Verwendung exit(EXIT_SUCCESS), die immer das Richtige tat.
Adrian McCarthy

1
5.1.2.2.3 "Wenn der Rückgabetyp der Hauptfunktion ein mit int kompatibler Typ ist, entspricht eine Rückgabe vom ersten Aufruf der Hauptfunktion dem Aufruf 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. "
Lundin

38

Beachten Sie, dass die Standards C und C ++ zwei Arten von Implementierungen definieren: freistehend und gehostet.

  • Von C90 gehostete Umgebung

    Zulässige Formulare 1 :

    int main (void)
    int main (int argc, char *argv[])
    
    main (void)
    main (int argc, char *argv[])
    /*... etc, similar forms with implicit int */

    Bemerkungen:

    Die beiden ersteren werden explizit als zulässige Formulare angegeben, die anderen sind implizit zulässig, da C90 "implizites int" für Rückgabetyp- und Funktionsparameter zulässt. Keine andere Form ist erlaubt.

  • C90 freistehende Umgebung

    Jede Form oder jeder Name von main ist erlaubt 2 .

  • Von C99 gehostete Umgebung

    Zulässige Formulare 3 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */

    Bemerkungen:

    C99 hat "implicit int" entfernt und main()ist daher nicht mehr gültig.

    Ein seltsamer, mehrdeutiger Satz "oder auf eine andere implementierungsdefinierte Weise" wurde eingeführt. Dies kann entweder als "die Parameter int main()können variieren" oder als "main kann jede implementierungsdefinierte Form haben" interpretiert werden .

    Einige Compiler haben sich entschieden, den Standard auf die letztere Weise zu interpretieren. Man kann wohl nicht leicht sagen, dass sie nicht streng konform sind, indem man den Standard an sich zitiert, da er nicht eindeutig ist.

    Es main()war jedoch wahrscheinlich (?) Nicht die Absicht dieses neuen Satzes , völlig wilde Formen von zuzulassen . Die C99-Begründung (nicht normativ) impliziert, dass sich der Satz auf zusätzliche Parameter zu int main 4 bezieht .

    Der Abschnitt zur Beendigung des Programms für gehostete Umgebungen wird dann weiter über den Fall diskutiert, in dem main nicht int 5 zurückgibt . Obwohl dieser Abschnitt nicht normativ dafür ist, wie main deklariert werden soll, impliziert er definitiv, dass main auch auf gehosteten Systemen vollständig implementierungsdefiniert deklariert werden kann.

  • C99 freistehende Umgebung

    Jede Form oder jeder Name von main ist erlaubt 6 .

  • Von C11 gehostete Umgebung

    Zulässige Formen 7 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */
  • C11 freistehende Umgebung

    Jede Form oder jeder Name von main ist zulässig 8 .


Beachten Sie, dass dies int main()in keiner der oben genannten Versionen als gültiges Formular für eine gehostete Implementierung von C aufgeführt wurde. In C im Gegensatz zu C ++ ()und (void)haben unterschiedliche Bedeutungen. Ersteres ist ein veraltetes Merkmal, das aus der Sprache entfernt werden kann. Siehe zukünftige Sprachanweisungen für C11:

6.11.6 Funktionsdeklaratoren

Die Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Parameterdeklaratoren im Prototypformat) ist eine veraltete Funktion.


  • Gehostete C ++ 03-Umgebung

    Zulässige Formen 9 :

    int main ()
    int main (int argc, char *argv[])

    Bemerkungen:

    Beachten Sie die leere Klammer im ersten Formular. C ++ und C unterscheiden sich in diesem Fall, da dies in C ++ bedeutet, dass die Funktion keine Parameter akzeptiert. In C bedeutet dies jedoch, dass ein beliebiger Parameter verwendet werden kann.

  • C ++ 03 freistehende Umgebung

    Der Name der beim Start aufgerufenen Funktion ist implementierungsdefiniert. Wenn es benannt ist main(), muss es den angegebenen Formularen 10 folgen :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])
  • Gehostete C ++ 11-Umgebung

    Zulässige Formulare 11 :

    int main ()
    int main (int argc, char *argv[])

    Bemerkungen:

    Der Text der Norm wurde geändert, hat aber die gleiche Bedeutung.

  • C ++ 11 freistehende Umgebung

    Der Name der beim Start aufgerufenen Funktion ist implementierungsdefiniert. Wenn es benannt ist main(), muss es den angegebenen Formularen 12 folgen :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])

Verweise

  1. ANSI X3.159-1989 2.1.2.2 Gehostete Umgebung. "Programmstart"

    Die beim Programmstart aufgerufene Funktion heißt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von int und ohne Parameter definiert werden:

    int main(void) { /* ... */ } 

    oder mit zwei Parametern (hier als argc und argv bezeichnet, obwohl beliebige Namen verwendet werden können, da sie für die Funktion, in der sie deklariert sind, lokal sind):

    int main(int argc, char *argv[]) { /* ... */ }
  2. ANSI X3.159-1989 2.1.2.1 Freistehende Umgebung:

    In einer freistehenden Umgebung (in der die Ausführung des C-Programms ohne Nutzen eines Betriebssystems erfolgen kann) sind Name und Typ der beim Programmstart aufgerufenen Funktion implementierungsdefiniert.

  3. ISO 9899: 1999 5.1.2.2 Gehostete Umgebung -> 5.1.2.2.1 Programmstart

    Die beim Programmstart aufgerufene Funktion heißt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von int und ohne Parameter definiert werden:

    int main(void) { /* ... */ } 

    oder mit zwei Parametern (hier als argc und argv bezeichnet, obwohl beliebige Namen verwendet werden können, da sie für die Funktion, in der sie deklariert sind, lokal sind):

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

    oder gleichwertig; 9) oder auf eine andere implementierungsdefinierte Weise.

  4. Begründung für den internationalen Standard - Programmiersprachen - C, Revision 5.10. 5.1.2.2 Gehostete Umgebung -> 5.1.2.2.1 Programmstart

    Das Verhalten der Argumente zu main und des Zusammenspiels von exit, main und atexit (siehe §7.20.4.2) wurde kodifiziert, um unerwünschte Unterschiede bei der Darstellung von argv-Zeichenfolgen und bei der Bedeutung der von main zurückgegebenen Werte einzudämmen.

    Die Angabe von argc und argv als Argumente für main erkennt umfangreiche frühere Praktiken an. argv [argc] muss ein Nullzeiger sein, um eine redundante Prüfung für das Ende der Liste bereitzustellen, auch auf der Grundlage der gängigen Praxis.

    main ist die einzige Funktion, die portabel entweder mit null oder zwei Argumenten deklariert werden kann. (Die Anzahl der Argumente anderer Funktionen muss genau zwischen Aufruf und Definition übereinstimmen.) In diesem Sonderfall wird lediglich die weit verbreitete Praxis erkannt, die Argumente für main wegzulassen, wenn das Programm nicht auf die Programmargumentzeichenfolgen zugreift. Während viele Implementierungen mehr als zwei Hauptargumente unterstützen, ist eine solche Praxis vom Standard weder gesegnet noch verboten. Ein Programm, das main mit drei Argumenten definiert, ist nicht streng konform (siehe §J.5.1.).

  5. ISO 9899: 1999 5.1.2.2 Gehostete Umgebung -> 5.1.2.2.3 Programmbeendigung

    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 Wert, den die Hauptfunktion als Argument zurückgibt; 11) Erreichen des }Endes Die Hauptfunktion gibt den Wert 0 zurück. Wenn der Rückgabetyp nicht mit int kompatibel ist, ist der an die Hostumgebung zurückgegebene Beendigungsstatus nicht angegeben.

  6. ISO 9899: 1999 5.1.2.1 Freistehende Umgebung

    In einer freistehenden Umgebung (in der die Ausführung des C-Programms ohne Nutzen eines Betriebssystems erfolgen kann) sind Name und Typ der beim Programmstart aufgerufenen Funktion implementierungsdefiniert.

  7. ISO 9899: 2011 5.1.2.2 Gehostete Umgebung -> 5.1.2.2.1 Programmstart

    Dieser Abschnitt ist identisch mit dem oben genannten C99.

  8. ISO 9899: 1999 5.1.2.1 Freistehende Umgebung

    Dieser Abschnitt ist identisch mit dem oben genannten C99.

  9. ISO 14882: 2003 3.6.1 Hauptfunktion

    Eine Implementierung darf die Hauptfunktion nicht vordefinieren. Diese Funktion darf nicht überlastet werden. Es muss einen Rückgabetyp vom Typ int haben, ansonsten ist sein Typ implementierungsdefiniert. Alle Implementierungen müssen beide der folgenden Definitionen von main zulassen:

    int main() { /* ... */ }

    und

    int main(int argc, char* argv[]) { /* ... */ }
  10. ISO 14882: 2003 3.6.1 Hauptfunktion

    Es ist implementierungsdefiniert, ob ein Programm in einer freistehenden Umgebung erforderlich ist, um eine Hauptfunktion zu definieren.

  11. ISO 14882: 2011 3.6.1 Hauptfunktion

    Eine Implementierung darf die Hauptfunktion nicht vordefinieren. Diese Funktion darf nicht überlastet werden. Es muss einen Rückgabetyp vom Typ int haben, ansonsten ist sein Typ implementierungsdefiniert. Alle Implementierungen müssen beides zulassen

    - eine Funktion von (), die int und zurückgibt

    - eine Funktion von (int, Zeiger auf Zeiger auf char), die int zurückgibt

    als Haupttyp (8.3.5).

  12. ISO 14882: 2011 3.6.1 Hauptfunktion

    Dieser Abschnitt ist identisch mit dem oben genannten C ++ 03.


Eine Frage: Bedeuten C ++ - Standards, dass die Signatur der Startfunktion in freistehenden Umgebungen ebenfalls implementiert ist? Zum Beispiel könnte eine Implementierung die Startfunktion wie folgt definiert haben: int my_startup_function ()oder int my_startup_function (int argc, char *argv[])kann sie zum Beispiel auch char my_startup_function (long argc, int *argv[])als Startfunktion haben? Ich denke nein, richtig? Ist das nicht auch mehrdeutig?
Utku

@Utku Es kann eine beliebige Signatur haben, solange es nicht benannt ist, main()da es dann eine der aufgelisteten Signaturen verwenden muss. Ich würde mir vorstellen, dass dies am häufigsten vorkommt void my_startup_function (), da es keinen Sinn macht, auf freistehenden Systemen vom Programm zurückzukehren.
Lundin

1
Aha. Wenn es jedoch erlaubt ist, einen Namen und eine Signatur für die Startfunktion zu verwenden, warum nicht auch eine andere Signatur verwenden main? Tut mir leid, wenn das keine kluge Frage ist, aber ich konnte die Gründe dafür nicht verstehen.
Utku

@Utku C und C ++ sind dort unterschiedlich. Ich habe keine Ahnung, warum C ++ dies erzwingt, es gibt keine Begründung. Ich vermute, der Hauptschuldige (Wortspiel beabsichtigt) ist Stroustrup, der früh erklärte, dass der Hauptschuldige int, Punkt zurückgeben muss. Denn als er die erste C ++ - Version erstellte, war er nur an gehostete Systeme gewöhnt. In dem verlinkten Beitrag scheint Stroustrup die Existenz freistehender Implementierungen immer noch nicht zu bemerken: Er verweist beispielsweise ignorant auf das Unterkapitel der gehosteten Implementierung des C-Standards und ignoriert die Existenz von Kapitel 5.1.2.1.
Lundin

1
Bemerkenswert am C11-Standardentwurf ist, dass func()der Entwurf selbst , obwohl er als veraltet angesehen wird, int main()in seinen eigenen Beispielen verwendet wird.
Antti Haapala

29

Bei Erfolg 0 zurückgeben und bei Fehler ungleich Null. Dies ist der Standard, der von UNIX- und DOS-Skripten verwendet wird, um herauszufinden, was mit Ihrem Programm passiert ist.


8

main() In C89 und K & R C sind nicht spezifizierte Rückgabetypen standardmäßig 'int'.

return 1? return 0?
  1. Wenn Sie keine return-Anweisung schreiben int main(), gibt der Abschluss {standardmäßig 0 zurück.

  2. return 0oder return 1wird vom übergeordneten Prozess empfangen. In einer Shell geht es in eine Shell-Variable, und wenn Sie Ihr Programm aus einer Shell ausführen und diese Variable nicht verwenden, müssen Sie sich keine Gedanken über den Rückgabewert von machen main().

Siehe Wie kann ich feststellen, was meine Hauptfunktion zurückgegeben hat? .

$ ./a.out
$ echo $?

Auf diese Weise können Sie sehen, dass es die Variable ist, $?die das niedrigstwertige Byte des Rückgabewerts von empfängt main().

In Unix- und DOS-Skripten return 0werden normalerweise bei Erfolg und bei Fehlern ungleich Null zurückgegeben. Dies ist der Standard, der von Unix- und DOS-Skripten verwendet wird, um herauszufinden, was mit Ihrem Programm passiert ist, und um den gesamten Ablauf zu steuern.


4
Genau genommen $?ist es keine Umgebungsvariable; Es ist eine vordefinierte (oder integrierte) Shell-Variable. Der Unterschied ist schwer zu erkennen, aber wenn Sie env(ohne Argumente) ausführen , wird die Umgebung gedruckt und $?nicht in der Umgebung angezeigt.
Jonathan Leffler

1
Die automatische Rückgabe von 0, wenn main "vom Ende fällt", ist nur in C ++ und C99 und nicht in C90 möglich.
Kaz

Tippfehler: "Schließen {" sollte sein }. SO erlaube ich mir nicht, eine so kleine Bearbeitung vorzunehmen.
Spencer

7

Beachten Sie, dass einige Betriebssysteme (Windows) den zurückgegebenen Wert auf ein einzelnes Byte (0-255) kürzen, obwohl Sie ein int zurückgeben.


4
Unix macht dasselbe wie wahrscheinlich die meisten anderen Betriebssysteme. Ich weiß, dass VMS so unglaubliche seltsame Dinge damit macht, dass die Rückgabe von etwas anderem als EXIT_SUCCESS oder EXIT_FAILURE um Ärger bittet.
Leon Timmermans

2
MSDN scheint sich zu unterscheiden: Wenn ein Exit-Code über mscorlib gemeldet wird, ist er eine vorzeichenbehaftete 32-Bit-Ganzzahl . Dies scheint zu implizieren, dass die C-Laufzeitbibliotheken , die Exit-Codes abschneiden, fehlerhaft sind.

Ja, das ist falsch. Unter Windows wird eine 32-Bit-Ganzzahl zurückgegeben (und in konvertiert unsigned). Dies gilt auch für UNIX-Systeme mit 32-Bit-Ganzzahlen. UNIX-Shells auf beiden Systemen behalten jedoch normalerweise nur eine vorzeichenlose 8-Bit-Ganzzahl bei.
John McFarlane

4

Der Rückgabewert kann vom Betriebssystem verwendet werden, um zu überprüfen, wie das Programm geschlossen wurde.

Der Rückgabewert 0 bedeutet normalerweise OK in den meisten Betriebssystemen (die mir sowieso einfallen).

Sie können auch überprüfen, wenn Sie einen Prozess selbst aufrufen und feststellen, ob das Programm beendet und ordnungsgemäß beendet wurde.

Es ist NICHT nur eine Programmierkonvention.


Die Frage enthält nichts, was darauf hindeutet, dass ein operatives System vorhanden ist. Die Rückgabe eines Wertes macht in einem freistehenden System keinen Sinn.
Lundin

3

Der Rückgabewert von main()zeigt an, wie das Programm beendet wurde. Wenn der Rückgabewert ist zero, bedeutet dies, dass die Ausführung erfolgreich war, während ein Wert ungleich Null darstellt, dass bei der Ausführung ein Fehler aufgetreten ist.


1
Dies ist ein Kommentar, keine Antwort auf die Frage.
Lundin

2

Ich hatte den Eindruck, dass Standard angibt, dass main keinen Rückgabewert benötigt, da eine erfolgreiche Rückgabe auf dem Betriebssystem basiert (Null in einem könnte entweder ein Erfolg oder ein Fehler in einem anderen sein), daher war das Fehlen einer Rückgabe ein Hinweis für die Compiler, um die erfolgreiche Rückgabe selbst einzufügen.

Normalerweise gebe ich jedoch 0 zurück.


Mit C99 (und C ++ 98) können Sie die return-Anweisung in main weglassen. In C89 können Sie die return-Anweisung nicht weglassen.
Jonathan Leffler

Dies ist ein Kommentar, keine Antwort.
Lundin

Dies gibt keine Antwort auf die Frage. Um einen Autor zu kritisieren oder um Klärung zu bitten, hinterlassen Sie einen Kommentar unter seinem Beitrag.
Steve Lillis

6
@SteveLillis: Im Jahr 2008 hatte SO keinen Kommentarbereich.
Graham.reeds

2

Die Rückgabe von 0 sollte dem Programmierer mitteilen, dass das Programm den Job erfolgreich beendet hat.


Wenn Sie 1 von den main()normalen Signalen zurückgeben, ist ein Fehler aufgetreten. Die Rückgabe von 0 signalisiert Erfolg. Wenn Ihre Programme immer fehlschlagen, ist 1 in Ordnung, aber nicht die beste Idee.
Jonathan Leffler

1
@ JonathanLeffler: Die Bedeutung der Rückkehr 1von mainist implementierungsdefiniert. Die einzige Sprache , definierte Werte sind 0, EXIT_SUCCESS(oft als definiert 0), und EXIT_FAILURE. return 1;Bezeichnet in OpenVMS eine erfolgreiche Beendigung.
Keith Thompson

VMS ist nicht "normal" - im Sinne von dem, was ich gesagt habe. Ist es nicht so etwas wie 'irgendein seltsamer Wert ist Erfolg; gerade Werte sind Fehler 'auf VMS?
Jonathan Leffler

2

Auslassen return 0

Wenn ein C- oder C ++ - Programm das Ende des mainCompilers erreicht, generiert es automatisch Code, um 0 zurückzugeben, sodass es nicht erforderlich ist, return 0;explizit am Ende von zu setzen main.

Hinweis: Wenn ich diesen Vorschlag mache, folgt fast immer eine von zwei Arten von Kommentaren: "Das wusste ich nicht." oder "Das ist ein schlechter Rat!" Mein Grundprinzip ist, dass es sicher und nützlich ist, sich auf das vom Standard explizit unterstützte Compilerverhalten zu verlassen. Für C seit C99; siehe ISO / IEC 9899: 1999, Abschnitt 5.1.2.2.3:

[...] eine Rückkehr vom ersten Aufruf der mainFunktion entspricht dem Aufruf der exitFunktion mit dem von der mainFunktion als Argument zurückgegebenen Wert ; Erreichen der} , das die mainFunktion beendet, wird der Wert 0 zurückgegeben.

Für C ++ seit dem ersten Standard im Jahr 1998; siehe ISO / IEC 14882: 1998, Abschnitt 3.6.1:

Wenn die Steuerung das Ende von main erreicht, ohne auf eine return-Anweisung zu stoßen, führt dies dazu, dass return 0 ausgeführt wird.

Alle Versionen beider Standards (C99 und C ++ 98) haben seitdem die gleiche Idee beibehalten. Wir verlassen uns auf automatisch generierte Elementfunktionen in C ++, und nur wenige Leute schreiben explizite return;Anweisungen am Ende einer voidFunktion. Gründe gegen das Weglassen scheinen sich auf "es sieht komisch aus" zu beschränken . Wenn Sie wie ich neugierig auf die Gründe für die Änderung des C-Standards sind lesen Sie diese Frage . Beachten Sie auch, dass dies in den frühen neunziger Jahren als "schlampige Praxis" angesehen wurde, da es sich zu dieser Zeit um undefiniertes Verhalten handelte (obwohl es weitgehend unterstützt wurde).

Darüber hinaus enthalten die C ++ - Kernrichtlinien mehrere Fälle des Auslassensreturn 0; am Ende mainund keine Instanzen, in denen eine explizite Rückgabe geschrieben wird. Obwohl es in diesem Dokument noch keine spezifische Richtlinie zu diesem speziellen Thema gibt, scheint dies zumindest eine stillschweigende Bestätigung der Praxis zu sein.

Also befürworte ich, es wegzulassen; andere sind anderer Meinung (oft vehement!). Wenn Sie auf Code stoßen, der ihn weglässt, wissen Sie auf jeden Fall, dass er vom Standard ausdrücklich unterstützt wird, und Sie wissen, was er bedeutet.


2
Dies ist ein schlechter Rat, da Compiler, die nur C89 implementieren, keinen späteren Standard, immer noch extrem verbreitet sind (ich schreibe dies 2017) und auf absehbare Zeit extrem häufig bleiben werden. Zum Beispiel habe ich zuletzt überprüft, dass keine Version der Microsoft-Compiler C99 implementiert hat, und ich verstehe, dass dies auch noch typisch für Compiler für eingebettete Systeme ist, die keine GCC sind.
zwol

4
@zwol: Jeder, der keine andere Wahl hat, als einen Compiler zu verwenden, der um 28 Jahre veraltet ist, hat wahrscheinlich mehr Probleme als zu entscheiden, ob er explizit einbezogen werden return 0;soll. Ich möchte jedoch darauf hinweisen, dass viele Compiler dieser Ära bereits ein implizites implementiert haben return 0;, bevor es war standardisiert.
Edward

2
Tatsächlich arbeite ich mit vielen eingebetteten Systemen und habe return 0seit über einem Jahrzehnt keinen Compiler mehr gefunden, der implizite nicht unterstützt . Auch aktuelle Versionen von Microsoft C unterstützen dies . Vielleicht sind Ihre Informationen nicht mehr aktuell?
Edward

2
Ich kann es begrüßen, dass dies in C fast umstritten ist (per @zwol). In C ++ ist jede Kontroverse darüber purer Unsinn.
Leichtigkeitsrennen im Orbit

2
@ Edward Ich habe nicht gesagt, dass die Kontroverse nicht existiert, ich sagte, es sei Unsinn: P
Leichtigkeitsrennen im Orbit

1

Was zurückgegeben werden soll, hängt davon ab, was Sie mit der ausführbaren Datei tun möchten. Wenn Sie Ihr Programm beispielsweise mit einer Befehlszeilen-Shell verwenden, müssen Sie 0 für einen Erfolg und eine Nicht-Null für einen Fehler zurückgeben. Dann könnten Sie das Programm in Shells mit bedingter Verarbeitung verwenden, abhängig vom Ergebnis Ihres Codes. Sie können auch einen beliebigen Wert ungleich Null gemäß Ihrer Interpretation zuweisen. Beispielsweise können bei kritischen Fehlern verschiedene Programm-Exit-Punkte ein Programm mit unterschiedlichen Exit-Werten beenden. Dies steht der aufrufenden Shell zur Verfügung, die entscheiden kann, was zu tun ist, indem sie den zurückgegebenen Wert überprüft. Wenn der Code nicht für die Verwendung mit Shells vorgesehen ist und der zurückgegebene Wert niemanden stört, wird er möglicherweise weggelassen. Ich persönlich benutze die Unterschriftint main (void) { .. return 0; .. }


Das Format von main () wird von der Implementierung bestimmt, dh vom Compiler. Der Programmierer wird nicht ausgewählt, welches Formular er auswählen soll, es sei denn, ein Compiler unterstützt mehrere Formulare.
Lundin

@Lundin Der Rückgabetyp wird von der Implementierung implementiert. Der zurückzugebende Wert wird jedoch vom Programmierer festgelegt. C99 In Abschnitt 5.1.2.2.3 wird erwähnt, dass der Rückgabetyp von mainkompatibel ist int. Daher ist die Rücksendung intkein Problem. Es sind zwar andere Rückgabetypen zulässig, aber in diesem Fall wird die Umgebungsvariable mit dem Rückgabewert nicht angegeben. Wenn ein Programmierer dies jedoch return 0;in Bash tut , kann er zum Erstellen von Zweigen verwendet werden.
Phoxis

1

Wenn Sie wirklich Probleme mit der Effizienz der Rückgabe einer Ganzzahl aus einem Prozess haben, sollten Sie es wahrscheinlich vermeiden, diesen Prozess so oft aufzurufen, dass dieser Rückgabewert zu einem Problem wird.

Wenn Sie dies tun (einen Prozess so oft aufrufen), sollten Sie eine Möglichkeit finden, Ihre Logik direkt im Aufrufer oder in einer DLL-Datei abzulegen, ohne jedem Aufruf einen bestimmten Prozess zuzuweisen. Die mehrfachen Prozesszuordnungen bringen Ihnen in diesem Fall das relevante Effizienzproblem.

Wenn Sie nur wissen möchten, ob die Rückgabe von 0 mehr oder weniger effizient ist als die Rückgabe von 1, kann dies in einigen Fällen vom Compiler abhängen, jedoch generisch, vorausgesetzt, sie werden aus derselben Quelle gelesen (lokal, Feld, Konstante, eingebettet) im Code, Funktionsergebnis usw.) erfordert es genau die gleiche Anzahl von Taktzyklen.


1

Hier ist eine kleine Demonstration der Verwendung von Rückkehrcodes ...

Wenn Sie die verschiedenen Tools verwenden, die das Linux-Terminal bereitstellt, können Sie den Rückkehrcode beispielsweise zur Fehlerbehandlung verwenden, nachdem der Vorgang abgeschlossen wurde. Stellen Sie sich vor, dass die folgende Textdatei myfile vorhanden ist:

Dies ist ein Beispiel, um zu überprüfen, wie grep funktioniert.

Wenn Sie den Befehl grep ausführen, wird ein Prozess erstellt. Sobald es fertig ist (und nicht kaputt gegangen ist), gibt es einen Code zwischen 0 und 255 zurück. Zum Beispiel:

$ grep order myfile

Wenn Sie tun

$ echo $?
$ 0

Sie erhalten eine 0. Warum? Weil grep eine Übereinstimmung gefunden und einen Exit-Code 0 zurückgegeben hat, was der übliche Wert für das erfolgreiche Beenden ist. Lassen Sie es uns noch einmal überprüfen, aber mit etwas, das nicht in unserer Textdatei enthalten ist und daher keine Übereinstimmung gefunden wird:

$ grep foo myfile
$ echo $?
$ 1

Da grep das Token "foo" nicht mit dem Inhalt unserer Datei abgleichen konnte, ist der Rückkehrcode 1 (dies ist der übliche Fall, wenn ein Fehler auftritt, aber wie oben angegeben, haben Sie viele Werte zur Auswahl).

Nun sollte das folgende Bash-Skript (geben Sie es einfach in ein Linux-Terminal ein), obwohl es sehr einfach ist, eine Vorstellung von der Fehlerbehandlung geben:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'
$ [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

Nach der zweiten Zeile wird nichts auf dem Terminal gedruckt, da "foo" grep auf 1 zurückgesetzt hat und wir prüfen, ob der Rückgabecode von grep gleich 0 war. Die zweite bedingte Anweisung gibt ihre Nachricht in der letzten Zeile wieder, da sie aufgrund von CHECK wahr ist == 1.

Wie Sie sehen können, wenn Sie diesen und jenen Prozess aufrufen, ist es manchmal wichtig zu sehen, was zurückgegeben wurde (anhand des Rückgabewerts von main ()).


In einem Shell-Skript würden Sie verwenden if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi- den Rückgabestatus direkt testen. Wenn Sie den Status erfassen möchten (für die Berichterstellung usw.), verwenden Sie eine Zuordnung. Sie könnten verwenden if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; fioder Sie könnten drei Zeilen verwenden. Sie können auch Optionen verwenden -sund -qauf grepdie Spiele oder Routine Fehlermeldungen erscheinen zu verhindern. Dies ist jedoch Shell Minutiae - der entscheidende Punkt, dass der Exit-Status nützlich sein kann - ist in Ordnung.
Jonathan Leffler

1

Was ist der richtige (effizienteste) Weg, um die main () - Funktion in C und C ++ zu definieren - int main () oder void main () - und warum?

Diese Worte "(am effizientesten)" ändern nichts an der Frage. Wenn Sie sich nicht in einer freistehenden Umgebung befinden, gibt es einen allgemein korrekten Weg, dies zu deklarieren main(), und das ist die Rückgabe von int.

Was sollte main()in C und C ++ zurückgegeben werden?

Es ist nicht das, was sollte main() zurückkehren, es ist , was tut main() Rückkehr. main()ist natürlich eine Funktion, die jemand anderes aufruft. Sie haben keine Kontrolle über den aufrufenden Code main(). Daher müssen Sie main()eine typkorrekte Signatur angeben, die mit dem Anrufer übereinstimmt. Sie haben einfach keine Wahl. Sie müssen sich nicht fragen, was mehr oder weniger effizient ist, was besser oder schlechter ist oder so etwas, denn die Antwort ist für Sie nach den Standards C und C + bereits perfekt definiert. Folge ihnen einfach.

Wenn int main (), dann 1 zurückgeben oder 0 zurückgeben?

0 für Erfolg, ungleich Null für Misserfolg. Auch hier müssen Sie nichts auswählen (oder auswählen): Es wird durch die Schnittstelle definiert, an die Sie sich anpassen sollen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.