Hay dudas sobre el uso exit
en C ++. La respuesta discute que no es una buena idea principalmente debido a RAII, por ejemplo, si exit
se llama en algún lugar del código, no se llamará a los destructores de objetos, por lo tanto, si, por ejemplo, un destructor estaba destinado a escribir datos en un archivo, esto no sucederá , porque no se llamó al destructor.
Me interesaba saber cómo es esta situación en C. ¿Son aplicables cuestiones similares también en C? Pensé que dado que en C no usamos constructores / destructores, la situación podría ser diferente en C. Entonces, ¿está bien usarlo exit
en C?
He visto funciones como las siguientes, que encuentro bien para usar en algunos casos, pero estaba interesado si tenemos problemas similares en C con el uso exit
, como se describe anteriormente con C ++. (lo que haría que usar funciones como las que se muestran a continuación no sea una buena idea).
void die(const char *message)
{
if(errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
SIGTERM
señal en POSIX y Linux ... Se espera que los servidores con buen comportamiento lo manejen bien. Y debe evitar el usoSIGKILL
(es decir, intentekill -TERM
entonceskill -QUIT
y solo más tardekill -KILL
como administrador de sistemas)exit()
función más votado no es pertinente (y el alto número de votos es sorprendente, no es una buena pregunta). ¿Por qué no debería usar la función exit () en C? Sería un buen candidato si tuviera respuestas del calibre de esta pregunta. No es así; un cierre inverso de eso como un duplicado de esto es apropiado, y lo he hecho.Respuestas:
En lugar de ello
abort()
, laexit()
función en C se considera una salida "elegante".De C11 (N1570) 7.22.4.4/p2 La función de salida (el énfasis es mío):
El Estándar también dice en 7.22.4.4/p4 que:
También vale la pena mirar los archivos 7.21.3 / p5 :
Sin embargo, como se menciona en los comentarios a continuación, no puede asumir que cubrirá todos los demás recursos , por lo que es posible que deba recurrir
atexit()
y definir devoluciones de llamada para su lanzamiento individualmente. De hecho, es exactamente lo queatexit()
se pretende hacer, como dice 7.22.4.2/p2 La función atexit :En particular, el estándar C no dice con precisión qué debería suceder con los objetos de duración de almacenamiento asignada (es decir
malloc()
), por lo que requiere que usted sepa cómo se hace en una implementación particular. Para los sistemas operativos modernos orientados a host, es probable que el sistema se encargue de ello, pero aún así, es posible que desee manejar esto usted mismo para silenciar los depuradores de memoria como Valgrind .fuente
stdio
, es decir, es equivalente a aclose()
en el FD. No sé en qué sentido @BlueMoon piensa que eso es incorrecto, pero no es menos incorrecto que una llamada aclose()
, y si se requiere una mayor aclaración, para eso es precisamenteatexit()
.atexit()
se puede usar. Usarse aexit()
sí mismo no es más brutal que hacerloreturn
desdemain()
.atexit
, de lo contrario, está bien. @BlueMoon: No creo que usar una función comodie
significa que uno no puede programar, en varias ocasiones uno podría querer usarla. De lo contrario, de hecho, también se puede manejar esto usando sólo los valores de retorno, etc.Sí, está bien usarlo
exit
en C.Para garantizar todos los búferes y un cierre ordenado y elegante, se recomienda utilizar esta función
atexit
, más información sobre esto aquíUn código de ejemplo sería así:
void cleanup(void){ /* example of closing file pointer and free up memory */ if (fp) fclose(fp); if (ptr) free(ptr); } int main(int argc, char **argv){ /* ... */ atexit(cleanup); /* ... */ return 0; }
Ahora, siempre que
exit
se llame, la funcióncleanup
se ejecutará, lo que puede albergar un cierre elegante, limpiar búferes, memoria, etc.fuente
No tiene constructores ni destructores, pero podría tener recursos (por ejemplo, archivos, flujos, sockets) y es importante cerrarlos correctamente. Un búfer no se pudo escribir de forma síncrona, por lo que salir del programa sin cerrar correctamente el recurso primero podría provocar daños.
fuente
exit()
(en lugar de_exit()
),atexit
se llaman las rutinas y elstdio
almacenamiento en búfer se descarga en el disco.exit()
está ahí precisamente para permitir una salida ordenada del programa.atexit
pueden ayudar, aunque no siempre son adecuadas.exit
si lo desea, pero es un salto global en el control que viene con todas las desventajas del control no estructurado que hemos discutido. Como una forma de terminar de una condición de falla, puede ser apropiado (y parece ser lo que quiere el OP), aunque para el flujo de control normal probablemente preferiría diseñar el bucle principal con una condición de salida adecuada para que realmente termine volviendo de main.El uso
exit()
está bienDos aspectos importantes del diseño de código que aún no se han mencionado son el "subproceso" y las "bibliotecas".
En un programa de un solo subproceso, en el código que está escribiendo para implementar ese programa, el uso
exit()
está bien. Mis programas lo usan de forma rutinaria cuando algo sale mal y el código no se recupera.Pero…
Sin embargo, llamar
exit()
es una acción unilateral que no se puede deshacer. Es por eso que tanto el "subproceso" como las "bibliotecas" requieren una reflexión cuidadosa.Programas enhebrados
Si un programa tiene varios subprocesos, entonces usar
exit()
es una acción dramática que termina todos los subprocesos. Probablemente sea inapropiado salir de todo el programa. Puede ser apropiado salir del hilo y reportar un error. Si conoce el diseño del programa, entonces tal vez esa salida unilateral esté permitida, pero en general, no será aceptable.Código de biblioteca
Y esa cláusula "consciente del diseño del programa" también se aplica al código de las bibliotecas. Rara vez es correcto llamar a una función de biblioteca de propósito general
exit()
. Estaría muy molesto si una de las funciones estándar de la biblioteca de C no regresara solo por un error. (Obviamente, las funciones comoexit()
,_Exit()
,quick_exit()
,abort()
no están destinados a retorno; que es diferente). Las funciones de la biblioteca C, por lo tanto, ya sea "no pueden fallar" o devolver una indicación de error de alguna manera. Si está escribiendo código para ir a una biblioteca de propósito general, debe considerar cuidadosamente la estrategia de manejo de errores para su código. Debe encajar con las estrategias de manejo de errores de los programas con los que se pretende usar, o el manejo de errores puede ser configurable.Tengo una serie de funciones de biblioteca (en un paquete con encabezado
"stderr.h"
, un nombre que camina sobre hielo fino) que están destinadas a salir ya que se usan para informar de errores. Esas funciones salen por diseño. Hay una serie de funciones relacionadas en el mismo paquete que informan de errores y no salen. Las funciones salientes se implementan en términos de las funciones no salientes, por supuesto, pero eso es un detalle de implementación interna.Tengo muchas otras funciones de biblioteca, y muchas de ellas se basan en el
"stderr.h"
código para informar de errores. Esa es una decisión de diseño que tomé y estoy de acuerdo. Pero cuando los errores se reportan con las funciones que salen, limita la utilidad general del código de la biblioteca. Si el código llama a las funciones de informe de errores que no salen, entonces las rutas del código principal en la función tienen que lidiar con los retornos de error de manera sensata: detectelos y transmita una indicación de error al código de llamada.El código para mi paquete de informes de errores está disponible en mi repositorio SOQ (Stack Overflow Questions) en GitHub como archivos
stderr.c
ystderr.h
en el subdirectorio src / libsoq .fuente
abort()
cuando es necesario asignar memoria pormalloc()
orealloc()
, Imagine, que tiene una aplicación, que se vincula con 100 bibliotecas y se pregunta cuál y cómo se bloqueó su aplicación. Es más, no he encontrado ninguna menciónabort()
en su documentación (pero no me malinterpreten. Es una gran biblioteca para su propósito).stderr
normalmente se almacena en búfer de línea. Si la salida termina con una nueva línea, el sistema la eliminará de todos modos.Una razón para evitar
exit
en funciones que no seanmain()
es la posibilidad de que su código se saque de contexto. Recuerde, la salida es un tipo de flujo de control no local . Como excepciones inalcanzables.Por ejemplo, puede escribir algunas funciones de administración de almacenamiento que se cierran en un error crítico del disco. Entonces alguien decide trasladarlos a una biblioteca. Salir de una biblioteca es algo que hará que el programa que realiza la llamada salga en un estado inconsistente para el que puede que no esté preparado.
O puede ejecutarlo en un sistema integrado. No hay ningún lugar para salir a , todo el asunto se ejecuta en un
while(1)
buclemain()
. Puede que ni siquiera esté definido en la biblioteca estándar.fuente
Dependiendo de lo que esté haciendo, salir puede ser la forma más lógica de salir de un programa en C. Sé que es muy útil para verificar que las cadenas de devoluciones de llamada funcionen correctamente. Tome este ejemplo de devolución de llamada que utilicé recientemente:
unsigned char cbShowDataThenExit( unsigned char *data, unsigned short dataSz,unsigned char status) { printf("cbShowDataThenExit with status %X (dataSz %d)\n", status, dataSz); printf("status:%d\n",status); printArray(data,dataSz); cleanUp(); exit(0); }
En el ciclo principal, configuro todo para este sistema y luego espero en un ciclo while (1). Es posible crear una bandera global para salir del ciclo while, pero esto es simple y hace lo que debe hacer. Si está tratando con búferes abiertos, como archivos y dispositivos, debe limpiarlos antes de cerrar para mantener la coherencia.
fuente
Es terrible en un gran proyecto cuando cualquier código puede salir excepto coredump. Trace es muy importante para mantener un servidor en línea.
fuente