Perché main non restituisce 0 qui?


116

Stavo solo leggendo

ISO / IEC 9899: 201x Committee Draft - 12 aprile 2011

in cui ho trovato in 5.1.2.2.3 Interruzione del programma

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

significa che se non specifichi alcuna istruzione return in main(), e se il programma viene eseguito correttamente, la parentesi graffa di chiusura} di main restituirà 0.

Ma nel codice seguente non specifico alcuna dichiarazione di ritorno, ma non restituisce 0

#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);
}

compilare

gcc test.c  
./a.out
sum is 15
echo $?
9          // here it should be 0 but it shows 9 why?

69
+1 per aver avuto la pazienza di leggere le specifiche .....
Asher

16
gccda solo (per la versione 4.6.2) compila un linguaggio molto simile ma non proprio come C. Compila GnuC89 - un linguaggio "vagamente" basato su C89.
pmg

2
Le parentesi sul returnnella dichiarazione sum()sono inutili. int main()dovrebbe essere int main(void).
Keith Thompson

24
Confusione! = Errore di battitura. Sulla mia tastiera '0' e 'o' sono abbastanza vicini da essere facilmente quest'ultimo. ;-)
The111

2
IMHO è una specifica abbastanza stupida, poiché obbliga il compilatore a gestire la funzione "main" in un modo speciale aggiungendo un "return 0" implicito. Quindi una funzione chiamata "main" si comporta in modo leggermente diverso. E i controlli in fase di compilazione ("nessun valore di ritorno" un simile)?
Giuseppe Guerrini

Risposte:


141

Questa regola è stata aggiunta nella versione del 1999 dello standard C. In C90, lo stato restituito non è definito.

Puoi abilitarlo passando -std=c99a gcc.

Come nota a margine, è interessante notare che viene restituito 9 perché è il ritorno di printfcui ha scritto solo 9 caratteri.


40
Oppure puoi semplicemente aggiungere return 0;prima della chiusura }. È innocuo e rende il tuo programma portabile su vecchi compilatori.
Keith Thompson

2
@ Mr.32: buona osservazione, printf () restituisce la lunghezza della stringa quindi è 9 che è il "ritorno" del main (senza usare -std = c99).
Hicham

1
@cnicutar: le funzioni in genere non restituiscono valori piccoli nello stack, perché comporterebbero un pop, un push e un salto piuttosto che solo una mossa e un ritorno, quindi è quasi certamente un registro, in eaxparticolare su x86.
Jon Purdy

8
Sì, l'API x86 di solito restituisce un valore intero tramite il eaxregistro. Vedi en.wikipedia.org/wiki/X86_calling_conventions#cdecl per ulteriori informazioni.
Sylvain Defresne

2
Molte volte ho visto codice come "int foo (void) {bar ();}", dove si intendeva "return bar ()". Questo codice funziona perfettamente sulla maggior parte dei processori, nonostante l'ovvio bug.
ugoren

15

Restituisce il valore di ritorno di printfcui è il numero di caratteri realmente stampati.


la domanda non parla di ciò che viene stampato nella console durante l'esecuzione del programma, parla del valore di ritorno del programma: (puoi ottenerlo in linux con il comando schell: echo $? e in Windows con: echo% errorlevel% )
Hicham

1
@eharvest Anche se non so come controllare %errorlevel%in Windows, c'è qualche differenza tra il codice di uscita e il valore di ritorno di mainin Linux?
Summer_More_More_Tea

1
@eharvest: La risposta non parla di ciò che viene stampato anche nella console. Parla del valore di ritorno del printfquale in questo caso è 9, che poi viene "promosso" come codice di uscita in mainqualche modo quando si usano certe versioni di gcc.
AH

spiacente. errore mio. hai ragione. Ho letto questa risposta troppo velocemente: /
Hicham

1
Nota che questo non è garantito. In C89 / C90, lo stato restituito in questo caso non è definito ; potrebbe essere qualsiasi cosa. Capita di restituire 9 perché il compilatore non ha fatto alcuno sforzo per restituire nient'altro. Altri compilatori probabilmente si comporteranno diversamente.
Keith Thompson

6

Il valore restituito da una funzione è normalmente memorizzato nel registro eax della cpu, quindi l'istruzione "return 4;" normalmente si compila in

mov eax, 4;
ret;

e restituire x (a seconda del compilatore) sarebbe qualcosa del tipo:

mov eax, [ebp + 4];
ret;

se non si specifica un valore di ritorno, il compilatore sputerà comunque il "ret" ma non cambia il valore di eax. Quindi il chiamante penserà che ciò che è mai stato lasciato nel registro eax in precedenza è il valore di ritorno. Per questo esempio sarebbe solitamente il valore di ritorno printf, ma compilatori diversi genereranno codice macchina diverso e utilizzeranno alcuni registri in modo diverso.

Questa è una spiegazione semplificata, diverse convenzioni di chiamata e piattaforme di destinazione giocheranno un ruolo fondamentale, ma dovrebbero essere sufficienti informazioni per spiegare cosa sta accadendo "dietro le quinte" nel tuo esempio.

Se hai una conoscenza di base dell'assemblatore, vale la pena confrontare lo smontaggio di diversi compilatori. Potresti scoprire che alcuni compilatori stanno cancellando il registro eax come salvaguardia.


11
Il compilatore potrebbe farlo. Non è definito. Potrebbe invece scegliere di spararti alla testa con una cartuccia della stampante.
Gare di leggerezza in orbita il

@LightnessRacesinOrbit undefined non equivale a imprevedibile, ovvero da "se il compilatore fa X, sarà conforme allo standard" non segue logicamente "il compilatore potrebbe fare X"
Owen

@ Owen: cita il passaggio standard che lo definisce.
Gare di leggerezza in orbita

2
@LightnessRacesinOrbit Mi dispiace di non seguirlo del tutto. Immagino che quello che voglio davvero dire è che penso che le intuizioni sotto il cofano come noggin182 fornite in questa risposta siano incredibilmente utili per il debug. Quando il tuo programma produce risultati inaspettati, molte volte non sai da dove provengono o nemmeno dove guardare nel codice, e avere una comprensione dei dettagli di implementazione può indirizzarti nella giusta direzione.
Owen

@Owen Non ho mai affermato diversamente
Lightness Races in Orbit
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.