¿Por qué main no devuelve 0 aquí?


116

Solo estaba leyendo

ISO / IEC 9899: Borrador del Comité 201x - 12 de abril de 2011

en el que encontré en 5.1.2.2.3 Terminación del programa

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

significa que si no especifica ninguna declaración de retorno en main(), y si el programa se ejecuta correctamente, en la llave de cierre} de main devolverá 0.

Pero en el siguiente código no especifico ninguna declaración de devolución, pero no devuelve 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);
}

compilar

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

69
+1 por tener la paciencia de leer las especificaciones .....
Asher

16
gccpor sí mismo (para la versión 4.6.2) compila un lenguaje muy similar pero no del todo parecido a C. Compila GnuC89 - un lenguaje "libremente" basado en C89.
pmg

2
Los paréntesis de la returndeclaración en sum()son innecesarios. int main()debería ser int main(void).
Keith Thompson

24
¡Confusión! = Error tipográfico. En mi teclado, '0' y 'o' están lo suficientemente cerca como para que sea fácilmente lo último. ;-)
The111

2
En mi humilde opinión es una especificación bastante estúpida, ya que obliga al compilador a gestionar la función "principal" de una manera especial añadiendo un "retorno 0" implícito. Entonces, una función llamada "principal" se comporta de una manera ligeramente diferente. ¿Qué pasa con las comprobaciones en tiempo de compilación ("sin valor de retorno" y similar)?
Giuseppe Guerrini

Respuestas:


141

Esa regla se agregó en la versión de 1999 del estándar C. En C90, el estado devuelto no está definido.

Puede habilitarlo pasando -std=c99a gcc.

Como nota al margen, curiosamente se devuelve 9 porque es el regreso del printfcual solo se escribieron 9 caracteres.


40
O simplemente puede agregar return 0;antes del cierre }. Es inofensivo y hace que su programa sea portátil para compiladores más antiguos.
Keith Thompson

2
@ Mr.32: buena observación, printf () devuelve la longitud de la cadena, por lo que es 9, que es el "retorno" de la principal (sin usar -std = c99).
Hicham

1
@cnicutar: Las funciones no suelen devolver valores pequeños en la pila, porque implicaría un pop, un empujón y un salto en lugar de solo un movimiento y un retorno, por lo que es casi seguro que sea un registro, eaxespecíficamente en x86.
Jon Purdy

8
Sí, la API x86 generalmente devuelve un valor entero a través del eaxregistro. Consulte en.wikipedia.org/wiki/X86_calling_conventions#cdecl para obtener más información.
Sylvain Defresne

2
Varias veces he visto código como "int foo (void) {bar ();}", donde se pretendía "return bar ()". Este código funciona bien en la mayoría de los procesadores, a pesar del error obvio.
ugoren

15

Devuelve el valor de retorno del printfcual es el número de caracteres realmente impresos.


la pregunta no habla de lo que se imprime en la consola al ejecutar el programa, habla del valor de retorno del programa: (puedes conseguirlo en linux con el comando schell: echo $? y en windows con: echo% errorlevel% )
Hicham

1
@eharvest Aunque no sé cómo verificar %errorlevel%en Windows, ¿alguna diferencia entre el código de salida y el valor de retorno de mainen Linux?
Summer_More_More_Tea

1
@eharvest: La respuesta no habla también de lo que está impreso en la consola. Habla sobre el valor de retornoprintf que en este caso es 9, que luego se "promueve" como el código de salida de mainalguna manera cuando se usan ciertas versiones de gcc.
AH

lo siento. mi error. tienes razón. Leí esta respuesta demasiado rápido: /
Hicham

1
Tenga en cuenta que esto no está garantizado. En C89 / C90, el estado devuelto en este caso no está definido ; podría ser cualquier cosa. Da la casualidad de que devuelve 9 porque el compilador no hizo ningún esfuerzo por devolver nada más. Es probable que otros compiladores se comporten de manera diferente.
Keith Thompson

6

El valor de retorno de una función normalmente se almacena en el registro eax de la CPU, por lo que la instrucción "return 4;" normalmente se compilaría para

mov eax, 4;
ret;

y devolver x (dependiendo de su compilador) sería algo como:

mov eax, [ebp + 4];
ret;

si no especifica un valor de retorno, el compilador todavía escupirá el "ret" pero no cambiará el valor de eax. Entonces, la persona que llama pensará que lo que quedó en el registro eax anteriormente es el valor de retorno. Para este ejemplo, normalmente sería el valor de retorno printf, pero diferentes compiladores generarán diferentes códigos de máquina y usarán algunos registros de manera diferente.

Esta es una explicación simplificada, diferentes convenciones de llamadas y plataformas de destino jugarán un papel vital, pero debería ser suficiente información para explicar lo que está sucediendo 'detrás de escena' en su ejemplo.

Si tiene un conocimiento básico del ensamblador, vale la pena comparar el desensamblaje de diferentes compiladores. Puede encontrar que algunos compiladores están limpiando el registro eax como medida de seguridad.


11
El compilador podría hacer eso. No está definido. En cambio, puede optar por dispararle en la cabeza con un cartucho de impresora.
Lightness Races in Orbit

@LightnessRacesinOrbit undefined no es lo mismo que impredecible, es decir, de "si el compilador hace X, cumplirá con el estándar" no sigue lógicamente "el compilador podría hacer X"
Owen

@Owen: Cita el pasaje estándar que define esto.
Lightness Races in Orbit

2
@LightnessRacesinOrbit Lo siento, no lo sigo del todo. Supongo que lo que realmente quiero decir es que creo que las ideas ocultas como noggin182 proporcionadas en esta respuesta son increíblemente útiles para la depuración. Cuando su programa produce resultados inesperados, muchas veces no sabe de dónde vienen o incluso en qué parte del código buscar, y comprender los detalles de la implementación puede orientarlo en la dirección correcta.
Owen

@Owen, nunca dije lo contrario
Lightness Races in Orbit
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.