Warum gibt main hier nicht 0 zurück?

116

Ich habe gerade gelesen

ISO / IEC 9899: 201x Ausschussentwurf - 12. April 2011

in dem ich unter 5.1.2.2.3 Programmbeendigung gefunden habe

..reaching the } that terminates the main function returns a value of 0. 

Dies bedeutet, dass wenn Sie keine return-Anweisung in angeben main()und das Programm erfolgreich ausgeführt wird, in der schließenden Klammer} von main 0 zurückgegeben wird.

Aber im folgenden Code gebe ich keine return-Anweisung an, aber es gibt keine 0 zurück

#include<stdio.h>
int sum(int a,int b)
{
return (a + b);
}

int main()
{
    int a=10;
    int b=5;
    int ans;    
    ans=sum(a,b);
    printf("sum is %d",ans);
}

kompilieren

gcc test.c  
./a.out
sum is 15
echo $?
9          // here it should be 0 but it shows 9 why?
Jeegar Patel
quelle
69
+1 für die Geduld, Spezifikationen zu lesen .....
Asher
16
gccselbst (für Version 4.6.2) kompiliert eine Sprache, die C sehr ähnlich, aber nicht ganz ähnlich ist. Sie kompiliert GnuC89 - eine Sprache, die "lose" auf C89 basiert.
PMG
2
Die Klammern in der returnAnweisung in sum()sind nicht erforderlich. int main()sollte sein int main(void).
Keith Thompson
24
Verwirrung! = Tippfehler. Auf meiner Tastatur sind '0' und 'o' nahe genug, um es leicht zu machen. ;-)
The111
2
IMHO ist eine ziemlich dumme Spezifikation, da sie den Compiler zwingt, die "Haupt" -Funktion auf besondere Weise zu verwalten, indem er eine implizite "Rückgabe 0" hinzufügt. Eine Funktion namens "main" verhält sich also etwas anders. Was ist mit den Überprüfungen zur Kompilierungszeit ("kein Rückgabewert" ähnlich)?
Giuseppe Guerrini

Antworten:

141

Diese Regel wurde in der 1999er Version des C-Standards hinzugefügt. In C90 ist der zurückgegebene Status undefiniert.

Sie können es aktivieren, indem Sie es -std=c99an gcc übergeben.

Als Randnotiz wird interessanterweise 9 zurückgegeben, da es die Rückgabe ist, von printfder gerade 9 Zeichen geschrieben wurden.

cnicutar
quelle
40
Oder Sie können einfach return 0;vor dem Schließen hinzufügen }. Es ist harmlos und macht Ihr Programm für ältere Compiler portierbar.
Keith Thompson
2
@ Mr.32: Gute Beobachtung, printf () gibt die Länge des Strings zurück, so dass es 9 ist, was die "Rückgabe" des Mains ist (ohne -std = c99 zu verwenden).
Hicham
1
@cnicutar: Funktionen geben normalerweise keine kleinen Werte auf dem Stapel zurück - da dies eher einen Pop, einen Push und einen Sprung als nur eine Bewegung und eine Rückgabe beinhalten würde -, daher handelt es sich mit ziemlicher Sicherheit um ein Register, eaxinsbesondere auf x86.
Jon Purdy
8
Ja, die x86-API gibt normalerweise einen ganzzahligen Wert über das eaxRegister zurück. Weitere Informationen finden Sie unter en.wikipedia.org/wiki/X86_calling_conventions#cdecl .
Sylvain Defresne
2
Ich habe mehrmals Code wie "int foo (void) {bar ();}" gesehen, wobei "return bar ()" beabsichtigt war. Dieser Code funktioniert auf den meisten Prozessoren trotz des offensichtlichen Fehlers einwandfrei.
Ugoren
15

Es gibt einen Rückgabewert zurück, printfdessen Anzahl der wirklich ausgedruckten Zeichen ist.

Summer_More_More_Tea
quelle
Die Frage bezieht sich nicht auf das, was in der Konsole gedruckt wird, wenn das Programm ausgeführt wird, sondern auf den Rückgabewert des Programms: (Sie können es unter Linux mit dem Befehl schell abrufen: echo $? und in Windows mit: echo% errorlevel% )
Hicham
1
@eharvest Obwohl ich nicht weiß, wie ich %errorlevel%in Windows einchecken soll , gibt es einen Unterschied zwischen dem Exit-Code und dem Rückgabewert von mainunter Linux?
Summer_More_More_Tea
1
@eharvest: Die Antwort spricht nicht darüber, was auch in der Konsole gedruckt wird. Es geht um den Rückgabewert, von printfdem in diesem Fall 9 ist, der dann mainbei Verwendung bestimmter gcc-Versionen irgendwie als Exit-Code "heraufgestuft" wird .
AH
Es tut uns leid. mein Fehler. Du hast recht. Ich habe diese Antwort zu schnell gelesen: /
Hicham
1
Beachten Sie, dass dies nicht garantiert ist. In C89 / C90 ist der in diesem Fall zurückgegebene Status undefiniert . es könnte alles sein. Es kommt zufällig 9 zurück, weil der Compiler keine Anstrengungen unternommen hat, etwas anderes zurückzugeben. Andere Compiler werden sich wahrscheinlich anders verhalten.
Keith Thompson
6

Der Rückgabewert einer Funktion wird normalerweise im eax-Register der CPU gespeichert, daher die Anweisung "return 4;" würde in der Regel zu kompilieren

mov eax, 4;
ret;

und return x (abhängig von Ihrem Compiler) wäre ungefähr so:

mov eax, [ebp + 4];
ret;

Wenn Sie keinen Rückgabewert angeben, spuckt der Compiler weiterhin das "ret" aus, ändert jedoch den Wert von eax nicht. Der Anrufer wird also denken, dass alles, was zuvor im eax-Register verblieben ist, der Rückgabewert ist. In diesem Beispiel ist dies normalerweise der Rückgabewert printf, aber verschiedene Compiler generieren unterschiedlichen Maschinencode und verwenden einige Register unterschiedlich.

Dies ist eine vereinfachte Erklärung. Unterschiedliche Aufrufkonventionen und Zielplattformen spielen eine wichtige Rolle. Es sollten jedoch genügend Informationen vorhanden sein, um zu erklären, was in Ihrem Beispiel „hinter den Kulissen“ geschieht.

Wenn Sie ein grundlegendes Verständnis von Assembler haben, lohnt es sich, die Demontage verschiedener Compiler zu vergleichen. Möglicherweise löschen einige Compiler das eax-Register als Schutz.

noggin182
quelle
11
Der Compiler könnte das tun. Es ist undefiniert. Es kann sich stattdessen dafür entscheiden, Sie mit einer Druckerpatrone in den Kopf zu schießen.
Leichtigkeitsrennen im Orbit
@LightnessRacesinOrbit undefined ist nicht dasselbe wie unvorhersehbar, dh von "Wenn der Compiler X ausführt, ist er standardkonform" folgt er nicht logisch "der Compiler könnte X
Owen
@ Owen: Zitieren Sie die Standardpassage, die dies definiert.
Leichtigkeitsrennen im Orbit
2
@LightnessRacesinOrbit Es tut mir leid, dass ich nicht ganz folge. Ich denke, was ich wirklich sagen möchte, ist, dass ich denke, dass unter der Haube liegende Erkenntnisse wie noggin182, die in dieser Antwort bereitgestellt werden, für das Debuggen unglaublich nützlich sind. Wenn Ihr Programm unerwartete Ergebnisse liefert, wissen Sie häufig nicht, woher sie kommen oder wo sie im Code zu suchen sind, und ein Verständnis der Implementierungsdetails kann Sie in die richtige Richtung weisen.
Owen
@Owen Ich habe nie etwas anderes behauptet
Leichtigkeitsrennen im Orbit