Pourquoi main ne renvoie pas 0 ici?

116

Je lisais juste

Projet de comité ISO / CEI 9899: 201x - 12 avril 2011

dans lequel j'ai trouvé sous 5.1.2.2.3 Résiliation du programme

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

cela signifie que si vous ne spécifiez aucune instruction return dans main(), et si le programme s'exécute avec succès, alors à l'accolade fermante} de main renverra 0.

Mais dans le code suivant, je ne spécifie aucune déclaration de retour, mais il ne renvoie pas 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);
}

compiler

gcc test.c  
./a.out
sum is 15
echo $?
9          // here it should be 0 but it shows 9 why?
Jeegar Patel
la source
69
+1 pour avoir la patience de lire les spécifications .....
Asher
16
gccpar lui-même (pour la version 4.6.2) compile un langage très similaire mais pas tout à fait semblable à C. Il compile GnuC89 - un langage "vaguement" basé sur C89.
pmg
2
Les parenthèses sur l' returninstruction dans sum()sont inutiles. int main()devrait être int main(void).
Keith Thompson
24
Confusion! = Faute de frappe. Sur mon clavier, «0» et «o» sont assez proches pour que ce soit facilement ce dernier. ;-)
The111
2
IMHO est une spécification assez stupide, car elle oblige le compilateur à gérer la fonction "main" d'une manière spéciale en ajoutant un "return 0" implicite. Ainsi, une fonction nommée "main" se comporte d'une manière légèrement différente. Qu'en est-il des vérifications au moment de la compilation ("aucune valeur de retour" et similaire)?
Giuseppe Guerrini

Réponses:

141

Cette règle a été ajoutée dans la version 1999 de la norme C. Dans C90, l'état renvoyé n'est pas défini.

Vous pouvez l'activer en passant -std=c99à gcc.

En passant, il est intéressant de noter que 9 est renvoyé car c'est le retour printfdont vient d'écrire 9 caractères.

cnicutar
la source
40
Ou vous pouvez simplement ajouter return 0;avant la fermeture }. C'est inoffensif et rend votre programme portable vers les anciens compilateurs.
Keith Thompson
2
@ Mr.32: bonne observation, printf () retourne la longueur de la chaîne donc c'est 9 qui est le "retour" du main (sans utiliser -std = c99).
Hicham
1
@cnicutar: Les fonctions ne renvoient généralement pas de petites valeurs sur la pile - car cela impliquerait un pop, un push et un saut plutôt qu'un simple mouvement et un retour - donc c'est presque certainement un registre, eaxspécifiquement sur x86.
Jon Purdy
8
Oui, l'API x86 renvoie généralement une valeur de type entier via le eaxregistre. Voir en.wikipedia.org/wiki/X86_calling_conventions#cdecl pour plus d'informations.
Sylvain Defresne
2
Plusieurs fois, j'ai vu du code comme "int foo (void) {bar ();}", où "return bar ()" était destiné. Ce code fonctionne très bien sur la plupart des processeurs, malgré le bogue évident.
ugoren
15

Il renvoie la valeur de retour printfdont le nombre de caractères réellement imprimés.

Summer_More_More_Tea
la source
la question ne parle pas de ce qui est imprimé dans la console lors de l'exécution du programme, elle parle de la valeur de retour du programme: (vous pouvez l'obtenir sous linux avec la commande schell: echo $? et sous windows avec: echo% errorlevel% )
Hicham
1
@eharvest Bien que je ne sache pas comment vérifier %errorlevel%dans Windows, une différence entre le code de sortie et la valeur de retour de maindans Linux?
Summer_More_More_Tea
1
@eharvest: La réponse ne parle pas non plus de ce qui est imprimé dans la console. Il parle de la valeur de retourprintf qui dans ce cas est 9 qui est alors "promu" comme code de sortie d'une mainmanière ou d'une autre lors de l'utilisation de certaines versions de gcc.
AH
Désolé. mon erreur. vous avez raison. J'ai lu cette réponse trop vite: /
Hicham
1
Notez que cela n'est pas garanti. Dans C89 / C90, l'état renvoyé dans ce cas n'est pas défini ; ça pourrait être n'importe quoi. Cela arrive à retourner 9 parce que le compilateur n'a fait aucun effort pour retourner autre chose. D'autres compilateurs se comporteront probablement différemment.
Keith Thompson
6

La valeur de retour d'une fonction est normalement stockée dans le registre eax du processeur, donc l'instruction "return 4;" compilerait généralement en

mov eax, 4;
ret;

et return x (selon votre compilateur) serait quelque chose comme:

mov eax, [ebp + 4];
ret;

si vous ne spécifiez pas de valeur de retour, le compilateur crachera toujours le "ret" mais ne changera pas la valeur de eax. Ainsi, l'appelant pensera que ce qui a déjà été laissé dans le registre eax est la valeur de retour. Pour cet exemple, ce serait généralement la valeur de retour printf, mais différents compilateurs généreront un code machine différent et utiliseront certains registres différemment.

Il s'agit d'une explication simplifiée, différentes conventions d'appel et plates-formes cibles joueront un rôle essentiel, mais elles devraient être suffisamment d'informations pour expliquer ce qui se passe «en coulisse» dans votre exemple.

Si vous avez une compréhension de base de l'assembleur, il vaut la peine de comparer le désassemblage de différents compilateurs. Vous constaterez peut-être que certains compilateurs effacent le registre eax à titre de sauvegarde.

noggin182
la source
11
Le compilateur pourrait faire cela. C'est indéfini. Il peut plutôt choisir de vous tirer une balle dans la tête avec une cartouche d'imprimante.
Courses de légèreté en orbite le
@LightnessRacesinOrbit undefined n'est pas la même chose qu'imprévisible, c'est-à-dire de "si le compilateur fait X, il sera conforme au standard" il ne suit pas logiquement "le compilateur pourrait faire X"
Owen
@Owen: Citez le passage standard définissant cela.
Courses de légèreté en orbite
2
@LightnessRacesinOrbit Je suis désolé de ne pas suivre tout à fait. Je suppose que ce que je veux vraiment dire, c'est que je pense que les informations sous le capot telles que noggin182 fournies dans cette réponse sont incroyablement utiles pour le débogage. Lorsque votre programme produit des résultats inattendus, vous ne savez souvent pas d'où ils viennent ni même où chercher dans le code, et avoir une compréhension des détails de l'implémentation peut vous orienter dans la bonne direction.
Owen
@Owen Je n'ai jamais prétendu le contraire
Courses de légèreté en orbite