Devrions-nous utiliser exit () en C?

Il est question sur l'utilisation de exit en C++. La réponse discute que ce n'est pas une bonne idée principalement à cause de RAII, par exemple, si exit est appelé quelque part dans le code, Les Destructeurs d'objets ne seront pas appelés, par conséquent, si par exemple un destructeur était censé écrire des données dans un fichier, cela n'arrivera pas, car le destructeur n'a pas été appelé.

J'étais intéressé comment cette situation dans C. des questions similaires s'appliquent-elles également dans C? Je pensais que depuis en C nous n'utilisons pas constructeurs / destructeurs, la situation peut être différente en C. Alors est-il correct d'utiliser exit en C?

J'ai vu des fonctions telles que ci-dessous, que je trouve bien à utiliser dans certains cas, mais était intéressé si nous avons des problèmes similaires en C avec l'utilisation de exit, comme décrit ci-dessus avec C++? (ce qui rendrait l'utilisation de fonctions telles que ci-dessous pas une bonne idée.).

void die(const char *message)
{
    if(errno) {
        perror(message);
    } else {
        printf("ERROR: %sn", message);
    }

    exit(1);
}
71
c
demandé sur Community 2015-07-19 15:21:59

7 réponses

Plutôt que abort(), la fonction exit() en C est considérée comme une sortie "gracieuse".

De C11 (N1570) 7.22.4.4/p2 La fonction de sortie (l'emphase est mienne):

La fonction exitprovoque la fin normale du programme {[21].

La norme dit aussi dans 7.22.4.4 / p4 que:

Ensuite, tous les flux ouverts avec données non écrites mises en mémoire tampon sont vidés , tous les flux ouverts sont fermés , et tous les fichiers créé par la fonction tmpfile sont supprimés.

Il vaut également la peine de regarder 7.21.3 / P5 Files :

Si la fonction main retourne à son appelant d'origine, ou si la fonction exit la fonction est appelée, tous les fichiers ouverts sont fermés (donc toutes les sorties les flux sont vidés) avant la fin du programme. Autres chemins vers la fin du programme, comme l'appel de la fonction abort, n'a pas besoin fermez tous les fichiers correctement.

Cependant, comme mentionné dans commentaires ci-dessous vous ne pouvez pas supposer qu'il couvrira toutes les autres ressources , donc vous devrez peut-être recourir à atexit() et définir des rappels pour leur libération individuellement. En fait, c'est exactement ce que atexit() est destiné à faire, comme il est dit dans 7.22.4.2 / p2 la fonction atexit :

Le atexit fonction enregistre la fonction pointée par func, pour être appelé sans arguments à la fin normale du programme.

Notamment, la norme C ne dit pas précisément ce qui devrait arriver aux objets de Durée de stockage allouée (c'est-à-dire malloc()), vous obligeant ainsi à être conscient de la façon dont cela est fait sur une implémentation particulière. Pour les systèmes d'exploitation modernes orientés hôte, il est probable que le système s'en occupe, mais vous voudrez peut-être gérer cela par vous-même afin de réduire au silence les débogueurs de mémoire tels que Valgrind.

81
répondu Grzegorz Szpetkowski 2015-07-19 19:40:06

Oui, il est correct d'utiliser exit en C.

Pour s'assurer que tous les tampons et gracieux arrêt, il serait recommandé d'utiliser cette fonction atexit, plus d'informations sur ce ici

Un exemple de code serait comme ceci:

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;
}

Maintenant, chaque fois que exit est appelée, la fonction cleanup sera exécutée, ce qui peut contenir un arrêt gracieux, un nettoyage des tampons, de la mémoire, etc.

22
répondu t0mm13b 2015-07-19 12:34:38

Vous n'avez pas de constructeurs et de destructeurs mais vous pourriez avoir resources (par exemple, fichiers, flux, sockets) et il est important de les fermer correctement. Un tampon n'a pas pu être écrit de manière synchrone, donc quitter le programme sans fermer correctement la ressource en premier, pourrait entraîner une corruption.

14
répondu enrico.bacis 2015-07-22 19:09:32

Utiliser exit() est OK

Deux aspects majeurs de la conception du code qui n'ont pas encore été mentionnés sont le "threading" et les "bibliothèques".

Dans un programme mono-thread, dans le code que vous écrivez pour implémenter ce programme, l'utilisation de exit() est correcte. Mes programmes l'utilisent régulièrement quand quelque chose a mal tourné et que le code ne va pas se rétablir.

Mais ...

Cependant, appeler exit() est une action unilatérale qui ne peut pas être annulée. C'est pourquoi 'Threading 'et ' bibliothèques' nécessite une réflexion approfondie.

Programmes filetés

Si un programme est multi-thread, l'utilisation de exit() est une action dramatique qui termine tous les threads. Il sera probablement inapproprié de quitter l'ensemble du programme. Il peut être approprié de quitter le thread, en signalant une erreur. Si vous êtes conscient de la conception du programme, alors peut-être que la sortie unilatérale est autorisée, mais en général, ce ne sera pas acceptable.

Code de la bibliothèque

Et ça la clause "consciente de la conception du programme" s'applique également au code dans les bibliothèques. Il est très rarement correct pour une fonction de bibliothèque à usage général d'appeler exit(). Vous seriez à juste titre contrarié si l'une des fonctions de bibliothèque C standard ne parvenait pas à retourner juste à cause d'une erreur. (Évidemment, les fonctions comme exit(), _Exit(), quick_exit(), abort() sont intention de ne pas revenir; c'est différent.) Les fonctions de la bibliothèque C sont donc "impossible d'échouer" ou renvoient une indication d'erreur d'une manière ou d'une autre. Si vous êtes écriture de code pour entrer dans une bibliothèque à usage général, vous devez examiner attentivement la stratégie de gestion des erreurs de votre code. Il devrait s'adapter aux stratégies de gestion des erreurs des programmes avec lesquels il est destiné à être utilisé, ou la gestion des erreurs peut être configurable.

J'ai une série de fonctions de bibliothèque (dans un paquet avec l'en-tête "stderr.h", un nom qui marche sur la glace mince) qui sont destinées à sortir car elles sont utilisées pour les rapports d'erreurs. Ces fonctions sortent par conception. Y sont une série connexe de fonctions dans le même package qui signalent des erreurs et ne sortent pas. Les fonctions sortantes sont implémentées en termes de fonctions non-sortantes, bien sûr, mais c'est un détail d'implémentation interne.

J'ai beaucoup d'autres fonctions de bibliothèque, et un bon nombre d'entre elles s'appuient sur le code "stderr.h" pour les rapports d'erreurs. C'est une décision de conception que j'ai prise et qui me convient. Mais lorsque les erreurs sont signalées avec les fonctions qui sortent, cela limite le général utilité le code de la bibliothèque. Si le code appelle les fonctions de rapport d'erreur qui ne sortent pas, alors les chemins de code principaux dans la fonction doivent traiter les retours d'erreur sanely-les détecter et relayer une indication d'erreur au code appelant.


Le code de mon paquet de rapport d'erreur est disponible dans mon dépôtSOQ (Stack Overflow Questions) sur GitHub en tant que fichiers stderr.c et stderr.h dans le sous-répertoiresrc/libsoq .

9
répondu Jonathan Leffler 2018-05-07 02:16:50

Une raison d'éviter exit dans des fonctions autres que main() est la possibilité que votre code soit sorti de son contexte. Rappelez-vous, exit est un type de flux de contrôle non local . Comme des exceptions insaisissables.

Par exemple, vous pouvez écrire certaines fonctions de gestion du stockage qui se terminent sur une erreur de disque critique. Puis quelqu'un décide de les déplacer dans une bibliothèque. Sortir d'une bibliothèque est quelque chose qui entraînera la sortie du programme appelant dans un état inconscient ce qui peut de ne pas être préparé pour.

, Ou vous pouvez l'exécuter sur un système embarqué. Il n'y a pas de sortie à, le tout s'exécute dans un while(1) boucle main(). Il peut même ne pas être défini dans la bibliothèque standard.

6
répondu pjc50 2015-07-19 16:58:52

Selon ce que vous faites, exit peut être le moyen le plus logique de sortir D'un programme en C. Je sais que c'est très utile pour vérifier que les chaînes de callbacks fonctionnent correctement. Prenez cet exemple de rappel que j'ai utilisé récemment:

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);
}

Dans la boucle principale, j'ai tout configuré pour ce système, puis j'attends dans une boucle while (1). Il est possible de créer un drapeau global pour quitter la boucle while à la place, mais c'est simple et fait ce qu'il doit faire. Si vous avez affaire à des tampons ouverts comme fichiers et périphériques vous devez les nettoyer avant la fermeture pour plus de cohérence.

2
répondu Dom 2015-07-19 18:49:45

C'est terrible dans un grand projet quand n'importe quel code peut sortir sauf pour coredump. Trace est très import pour maintenir un serveur en ligne.

-1
répondu user4531555 2015-07-22 17:37:55