return statement vs exit () en main ()

¿Debería usar exit() o simplemente return declaraciones en main() ? Personalmente, estoy a favor de las declaraciones de return porque siento que es como leer cualquier otra función y el control de flujo cuando estoy leyendo el código es sencillo (en mi opinión). E incluso si quiero refactorizar la función main() , tener return parece una mejor opción que exit() .

¿ exit() hace algo especial que el return no hace?

En realidad, hay una diferencia, pero es sutil. Tiene más implicaciones para C ++, pero las diferencias son importantes.

Cuando llamo return en main() , se invocarán destructores para mis objetos de ámbito local. Si llamo a exit() , no se llamará a ningún destructor para mis objetos de ámbito local. Vuelve a leer eso. exit() no regresa . Eso significa que una vez que lo llamo, no hay “backsies”. Cualquier objeto que hayas creado en esa función no será destruido. A menudo, esto no tiene ninguna implicación, pero a veces lo hace, como cerrar archivos (¿seguro que quieres que todos tus datos se vacíen en el disco?).

Tenga en cuenta que static objetos static se limpiarán incluso si llama a exit() . Finalmente, tenga en cuenta que si usa abort() , no se destruirán objetos. Es decir, ningún objeto global, ningún objeto estático ni ningún objeto local tendrá sus destructores llamados.

Proceda con precaución al favorecer la salida sobre el retorno.

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a

Otra diferencia: exit es una función de biblioteca estándar, por lo que debe incluir encabezados y un enlace con la biblioteca estándar. Para ilustrar (en C ++), este es un progtwig válido:

 int main() { return 0; } 

pero para usar exit necesitarás incluir:

 #include  int main() { exit(EXIT_SUCCESS); } 

Además, esto agrega una suposición adicional: que llamar a la exit de main tiene los mismos efectos secundarios que devolver cero. Como han señalado otros, esto depende del tipo de ejecutable que estés creando (es decir, quién llama a main ). ¿Estás codificando una aplicación que usa C-runtime? ¿Un plugin maya? Un servicio de Windows? ¿Un conductor? Cada caso requerirá una investigación para ver si la exit es equivalente a return . En mi humilde opinión, usar la exit cuando realmente quieres decir “ return hace que el código sea más confuso. OTOH, si realmente quieres decir exit , entonces, por supuesto, úsalo.

Hay al menos una razón para preferir la exit : si alguno de sus manejadores de atexit refiere a los datos de duración de almacenamiento automático en main , o si usó setvbuf o setbuf para asignar a uno de los streams estándar un buffer de duración de almacenamiento automático en main , luego regresar de main produce un comportamiento indefinido, pero llamar a exit es válido.

Otro uso potencial (usualmente reservado para progtwigs de juguete, sin embargo) es salir de un progtwig con invocaciones recursivas de main .

Siempre uso return porque el prototipo estándar para main() dice que devuelve un int .

Dicho esto, algunas versiones de los estándares dan el tratamiento especial main y suponen que devuelve 0 si no hay una statement de return explícita. Dado el siguiente código:

 int foo() {} int main(int argc, char *argv[]) {} 

G ++ solo genera una advertencia para foo() e ignora el retorno que falta desde main :

 % g++ -Wall -c foo.cc foo.cc: In function 'int foo()': foo.cc:1: warning: control reaches end of non-void function 

Recomiendo encarecidamente el comentario de R. sobre el uso de exit () para evitar tener almacenamiento automático en main() recuperado antes de que el progtwig termine realmente. Un return X; La instrucción en main() no es precisamente equivalente a una llamada para exit(X); , ya que el almacenamiento dynamic de main() desaparece cuando main() regresa, pero no desaparece si se realiza una llamada a exit() .

Además, en C o cualquier lenguaje similar a C, una statement de return sugiere fuertemente al lector que la ejecución continuará en la función de llamada, y que esta continuación de la ejecución suele ser técnicamente cierta si cuenta la rutina de inicio C que llamó a su main() función, no es exactamente lo que quiere decir cuando quiere terminar el proceso.

Después de todo, si desea finalizar su progtwig desde cualquier otra función, excepto main() , debe llamar a exit() . Si lo hace de manera uniforme en main() también hace que su código sea mucho más legible, y también hace que sea mucho más fácil para cualquier persona volver a factorizar su código; es decir, el código copiado de main() a alguna otra función no se comportará mal debido a declaraciones de return accidentales que deberían haber sido llamadas exit() .

Entonces, combinando todos estos puntos juntos, la conclusión es que es un mal hábito , al menos para C, usar una statement de return para finalizar el progtwig en main() .

¿Salir () hace algo especial que ‘retorno’ no hace?

Con algunos comstackdores para plataformas poco comunes, exit() podría traducir su argumento en el valor de salida de su progtwig, mientras que un retorno desde main() podría pasar el valor directamente al entorno de host sin ninguna traducción.

El estándar requiere un comportamiento idéntico en estos casos (específicamente, dice que devolver algo que sea compatible con int de main() debería ser equivalente a llamar a exit() con ese valor). El problema es que diferentes sistemas operativos tienen diferentes convenciones para interpretar los valores de salida. En muchos (¡MUCHOS!) Sistemas, 0 significa éxito y cualquier otra cosa es un fracaso. Pero en, por ejemplo, VMS, los valores impares significan éxito e incluso los que significan fracaso. Si devolvió 0 desde main() , un usuario de VMS vería un mensaje desagradable sobre una infracción de acceso. En realidad, no había una infracción de acceso: simplemente era el mensaje estándar asociado con el código de falla 0.

Entonces ANSI apareció y bendijo a EXIT_SUCCESS y EXIT_FAILURE como argumentos que podría pasar a exit() . El estándar también dice que la exit(0) debe comportarse de manera idéntica para exit(EXIT_SUCCESS) , por lo que la mayoría de las implementaciones definen EXIT_SUCCESS en 0 .

El estándar, por lo tanto, te pone en un aprieto en VMS, ya que no deja una forma estándar de devolver un código de falla que tiene el valor 0.

El comstackdor VAX / VMS C de principios de la década de 1990, por lo tanto, no interpretó el valor de retorno de main() , simplemente devolvió cualquier valor al entorno de host. Pero si utilizó exit() , haría lo que requería el estándar: traducir EXIT_SUCCESS (o 0 ) en un código de éxito y EXIT_FAILURE en un código de falla genérico. Para utilizar EXIT_SUCCESS , debe pasarlo a exit() , no puede devolverlo desde main() . No sé si las versiones más modernas de ese comstackdor preservaron ese comportamiento.

Un progtwig C portátil solía verse así:

 #include  #include  int main() { printf("Hello, World!\n"); exit(EXIT_SUCCESS); /* to get good return value to OS */ /*NOTREACHED*/ /* to silence lint warning */ return 0; /* to silence compiler warning */ } 

Aparte: si recuerdo bien, la convención VMS para valores de salida tiene más matices que impar / par. De hecho, utiliza algo así como los tres bits bajos para codificar un nivel de gravedad. En términos generales, sin embargo, los niveles de gravedad impar indicaron éxito o información variada e incluso los errores indicados.