¿Hay alguna diferencia entre return n
(en la main
función) y exit(n)
en C? ¿Está definido por los estándares C o POSIX o depende del sistema operativo o del compilador?
¿Hay alguna diferencia entre return n
(en la main
función) y exit(n)
en C? ¿Está definido por los estándares C o POSIX o depende del sistema operativo o del compilador?
Respuestas:
En la mayoría de los casos, no hay diferencia, pero aquí hay un programa en C que probablemente se comportará de manera diferente dependiendo de si usa return 0;
o exit(0);
:
#include <stdio.h>
#include <stdlib.h>
static char *message;
void cleanup(void) {
printf("message = \"%s\"\n", message);
}
int main(void) {
char local_message[] = "hello, world";
message = local_message;
atexit(cleanup);
#ifdef USE_EXIT
puts("exit(0);");
exit(0);
#else
puts("return 0;");
return 0;
#endif
}
Debido a la atexit()
llamada, exit(0);
o return 0;
hace cleanup
que se invoque la función. La diferencia es que si el programa llama exit(0);
, la limpieza ocurre mientras la "llamada" a main()
todavía está activa, por lo que el local_message
objeto todavía existe. return 0;
Sin embargo, la ejecución finaliza inmediatamente la invocación de main()
y luego invoca la cleanup()
función. Como se cleanup()
refiere (a través del message
puntero global ) a un objeto asignado localmente main
y ese objeto ya no existe, el comportamiento no está definido.
Aquí está el comportamiento que veo en mi sistema:
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
Ejecutar el programa sin -DUSE_EXIT
podría hacer nada, incluso bloquearse o imprimir "hello, world"
(si la memoria utilizada por local_message
no se golpea).
Sin embargo, en la práctica, esta diferencia solo se muestra si los objetos definidos localmente en el interior main()
se hacen visibles main()
al guardarlos apuntadores. Esto podría suceder plausiblemente argv
. (El experimento en mi sistema muestra que los objetos señalados por argv
y por *argv
seguir existiendo después de regresar main()
, pero no debe depender de eso).
Para C,
el Estándar dice que un retorno de la llamada inicial a main es equivalente a llamar a exit. Sin embargo, no se puede esperar que un retorno de main funcione si los datos locales a main pueden ser necesarios durante la limpieza.
Para C ++
Cuando se usa exit (0) para salir del programa, no se llama a los destructores de objetos no estáticos con ámbito local. Pero se llama a los destructores si se usa el retorno 0.
Programa 1 - - usa la salida (0) para salir
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
getchar();
}
};
int main() {
Test t1;
// using exit(0) to exit from main
exit(0);
}
Salida: Constructor de prueba interior
Programa 2: utiliza el retorno 0 para salir
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
}
};
int main() {
Test t1;
// using return 0 to exit from main
return 0;
}
Salida: Constructor de
prueba interna Destructor de prueba interna
Llamar a los destructores a veces es importante, por ejemplo, si el destructor tiene código para liberar recursos como cerrar archivos.
Tenga en cuenta que los objetos estáticos se limpiarán incluso si llamamos a exit (). Por ejemplo, vea el siguiente programa.
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
getchar();
}
};
int main() {
static Test t1; // Note that t1 is static
exit(0);
}
Salida: Constructor de
prueba interna Destructor de prueba interna
finally
Vale la pena señalar que el estándar C (C99) define dos tipos de entornos de ejecución, Entorno independiente y Entorno hospedado . El entorno independiente es un entorno C que no admite las bibliotecas C y está destinado a aplicaciones integradas y similares. El entorno de CA que admite las bibliotecas C se denomina entorno hospedado.
C99 dice que, en un entorno independiente, la terminación del programa se define como implementación. Entonces, si la implementación define main
, return n
y exit
, sus comportamientos son como se define en esa implementación.
C99 define el comportamiento del entorno alojado como,
Si el tipo de retorno de la función principal es un tipo compatible con él, un retorno de la llamada inicial a la función principal es equivalente a llamar a la función de salida con el valor devuelto por la función principal como argumento; alcanzar el} que finaliza la función principal devuelve un valor de 0. Si el tipo de retorno no es compatible con int, el estado de terminación devuelto al entorno host no está especificado.
Desde la perspectiva del estándar C, no realmente, aparte de return
ser una declaración y exit()
una función. Cualquiera de las dos hará atexit()
que se llame a cualquier función registrada, seguida de la finalización del programa.
Hay un par de situaciones que debes tener en cuenta:
main()
. Aunque rara vez se ve en la práctica, es legal en C. (C ++ lo prohíbe explícitamente).main()
. A veces, un existente main()
cambiará de nombre y se llamará por uno nuevo main()
.El uso de exit()
introducirá un error si cualquiera de esos ocurre después de que haya escrito el código, especialmente si no termina de manera anormal. Para evitar eso, es una buena idea tener el hábito de tratarlo main()
como la función que es y usarlo return
cuando desee que termine.