¿Hay alguna diferencia entre return n y exit (n) en C?

9

¿Hay alguna diferencia entre return n(en la mainfunción) y exit(n)en C? ¿Está definido por los estándares C o POSIX o depende del sistema operativo o del compilador?

Thomas Owens
fuente

Respuestas:

5

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 cleanupque 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_messageobjeto 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 messagepuntero global ) a un objeto asignado localmente mainy 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_EXITpodría hacer nada, incluso bloquearse o imprimir "hello, world"(si la memoria utilizada por local_messageno 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 argvy por *argvseguir existiendo después de regresar main(), pero no debe depender de eso).

Keith Thompson
fuente
16
  • 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

Vaibhav Agarwal
fuente
Cerrar archivos no es realmente un buen ejemplo de un destructor importante para disparar cuando salga, porque de todos modos los archivos se cerrarán al salir del programa.
Winston Ewert
1
@ WinstonEwert: Cierto, pero puede haber búferes de nivel de aplicación que aún necesitan ser vaciados.
Philipp
1
La pregunta no menciona C ++ en ningún lado ...
tdammers
No conozco ninguno de los dos idiomas, así que perdóname, pero esta respuesta me hace pensar que exit es como C # 's failfast, por lo que en C ++, ¿sale en un intento ejecutar un finalmente?
Jimmy Hoffa
@JimmyHoffa, c ++ no tienefinally
Winston Ewert
6

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 ny 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.

El d
fuente
1
Y realmente no tiene ningún sentido llamar a exit () desde un entorno independiente. El equivalente incrustado de exit () sería colgar el programa en un bucle eterno y luego esperar a que se agote el tiempo de vigilancia.
0

Desde la perspectiva del estándar C, no realmente, aparte de returnser 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:

  • La recursión en main(). Aunque rara vez se ve en la práctica, es legal en C. (C ++ lo prohíbe explícitamente).
  • Reutilización de 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 returncuando desee que termine.

Blrfl
fuente