web-dev-qa-db-fra.com

déclaration de retour vs exit () dans main ()

Devrais-je utiliser les instructions exit() ou simplement return dans main()? Personnellement, je préfère les instructions return, car j’ai le sentiment que c’est comme si on lisait une autre fonction et que le contrôle de flux lorsque je lis le code est fluide (à mon avis). Et même si je veux refactoriser la fonction main(), avoir return semble être un meilleur choix que exit().

Est-ce que exit() fait quelque chose de spécial que return ne fait pas?

186
Srikanth

En fait, il y a est une différence, mais c'est subtile. Cela a plus d'implications pour C++, mais les différences sont importantes.

Lorsque j'appelle return dans main(), les destructeurs sont appelés pour mes objets localement ciblés. Si j'appelle exit(), , aucun destructeur ne sera appelé pour mes objets localement ciblés! Relisez-le. exit() ne renvoie pas . Cela signifie qu'une fois que je l'appelle, il n'y a "pas de backsies". Tous les objets que vous avez créés dans cette fonction ne seront pas détruits. Souvent, cela n’a pas d’implications, mais parfois, comme la fermeture de fichiers (vous voulez sûrement que toutes vos données soient vidées sur le disque?).

Notez que les objets static seront nettoyés même si vous appelez exit(). Notez enfin que si vous utilisez abort(), aucun objet ne sera détruit. C'est-à-dire qu'aucun objet global, aucun objet statique et aucun objet local n'auront leurs destructeurs appelés.

Procédez avec prudence lorsque vous privilégiez la sortie au retour.

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

267
FreeMemory

Une autre différence: exit est une fonction de bibliothèque standard, vous devez donc inclure des en-têtes et établir un lien avec la bibliothèque standard. Pour illustrer (en C++), il s’agit d’un programme valide:

int main() { return 0; }

mais pour utiliser exit, vous aurez besoin d'un include:

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

De plus, cela ajoute une hypothèse supplémentaire: l'appel de exit de main a les mêmes effets secondaires que de renvoyer zéro. Comme d'autres l'ont souligné, cela dépend du type d'exécutable que vous construisez (c'est-à-dire qui appelle main). Êtes-vous en train de coder une application qui utilise le C-runtime? Un plugin Maya? Un service Windows? Un conducteur? Chaque cas nécessitera des recherches pour voir si exit est équivalent à return. IMHO en utilisant exit lorsque vous vraiment méchantreturn rend le code encore plus confus. OTOH, si vous signifie vraimentexit, alors utilisez-le.

24
jwfearn

Il y a au moins une raison de préférer exit: si l'un de vos gestionnaires atexit fait référence à des données de durée de stockage automatique dans main, ou si vous avez utilisé setvbuf ou setbuf pour affecter à l'un des flux standard un tampon de durée de stockage automatique dans main, puis le retour de main produit un comportement indéfini, mais l'appel de exit est valide.

Une autre utilisation potentielle (généralement réservée aux programmes de jouets cependant) est de sortir d'un programme avec des invocations récursives de main.

16
R..

Est-ce que exit () fait quelque chose de spécial que 'return' ne fait pas?

Avec certains compilateurs de plates-formes inhabituelles, exit() peut traduire son argument en valeur de sortie de votre programme, tandis qu'un retour de main() pourrait simplement passer la valeur directement à l'environnement hôte sans traduction.

La norme exige un comportement identique dans ces cas (en particulier, elle indique que le renvoi de quelque chose compatible avec int- de main() devrait être équivalent à l'appel de exit() avec cette valeur). Le problème est que différents systèmes d’exploitation ont des conventions différentes pour interpréter les valeurs de sortie. Sur de nombreux systèmes (BEAUCOUP!), 0 signifie succès et toute autre solution est un échec. Mais, disons, sur VMS, les valeurs impaires sont synonymes de succès et les plus équitables, d’échec. Si vous avez renvoyé 0 de main(), un utilisateur de VMS verrait un mauvais message concernant une violation d'accès. Il n'y avait pas réellement de violation d'accès - c'était simplement le message standard associé au code d'échec 0.

Ensuite, ANSI est arrivé et a béni EXIT_SUCCESS Et EXIT_FAILURE Comme arguments que vous pourriez transmettre à exit(). La norme indique également que exit(0) doit se comporter de manière identique à exit(EXIT_SUCCESS), de sorte que la plupart des implémentations définissent EXIT_SUCCESS Sur 0.

Le standard vous met donc dans une impasse sur VMS, car il ne laisse aucun moyen standard de renvoyer un code échec qui a la valeur 0.

Le compilateur C VAX/VMS du début des années 90 n’a donc pas interprété la valeur de retour de main(), il a simplement renvoyé une valeur quelconque à l’environnement hôte. Mais si vous utilisiez exit(), il ferait ce que la norme exige: traduire EXIT_SUCCESS (Ou 0) En un code de réussite et EXIT_FAILURE En un échec générique. code. Pour utiliser EXIT_SUCCESS, Vous aviez pour le passer à exit(), vous ne pouviez pas le retourner à partir de main(). Je ne sais pas si des versions plus modernes de ce compilateur ont préservé ce comportement.

Un programme C portable ressemblait à ceci:

#include <stdio.h>
#include <stdlib.h>

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 */
}

A part: Si je me souviens bien, la convention VMS pour les valeurs de sortie est plus nuancée que celle de pair/impair. En réalité, il utilise quelque chose comme les trois bits inférieurs pour coder un niveau de gravité. En règle générale, cependant, les niveaux de gravité impairs indiquaient le succès ou des informations diverses et les erreurs paires indiquaient des erreurs.

5
Adrian McCarthy

I FORTEMENT second le commentaire de R. sur l'utilisation de exit () afin d'éviter d'avoir la mémoire automatique dans main() récupérée avant la fin du programme . Une déclaration return X; Dans main() n'est pas précisément équivalente à un appel à exit(X);, car le stockage dynamique de main() disparaît lorsque main() est renvoyé. , mais il ne disparaît pas si un appel à exit() est effectué à la place.

De plus, en C ou dans un langage similaire à C, une instruction return indique clairement au lecteur que l'exécution se poursuivra dans la fonction appelante, et que cette poursuite est en général techniquement vraie si vous comptez la routine de démarrage en C qui appelé votre fonction main(), ce n'est pas exactement ce que vous voulez quand vous voulez mettre fin au processus.

Après tout, si vous voulez terminer votre programme depuis une autre fonction que main(), vous devez appeler exit(). Faire ainsi de manière cohérente dans main() rend également votre code beaucoup plus lisible, et facilite également la restructuration de votre code par quiconque. C'est-à-dire que le code copié de main() vers une autre fonction ne se comportera pas mal à cause de déclarations accidentelles return qui auraient dû avoir été exit() appelle.

Donc, en combinant tous ces points, la conclusion est qu’il s’agit d’une mauvaise habitude , au moins pour C, d’utiliser une instruction return terminer le programme dans main().

5
Greg A. Woods

J'utilise toujours return car le prototype standard de main() indique qu'il renvoie un int.

Cela dit, certaines versions des normes accordent un traitement spécial à main et supposent qu’elle renvoie 0 s’il n’ya pas d’instruction explicite return. Étant donné le code suivant:

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

G ++ génère uniquement un avertissement pour foo() et ignore le retour manquant de main:

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

En C, revenir de main revient exactement à appeler exit avec la même valeur.

La section 5.1.2.2.3 du norme C se lit comme suit:

Si le type de retour de la fonction principale est un type compatible avec int, , le retour de l'appel initial vers la fonction principale équivaut à l'appel de la fonction exit avec la valeur renvoyée par la fonction principale en tant que son argument ; 11) atteindre le} qui termine la fonction principale renvoie 0. Si le type de retour n'est pas compatible avec int, l'état de la terminaison renvoyé à l'environnement hôte n'est pas spécifié.

Les règles pour C++ sont un peu différentes, comme mentionné dans d'autres réponses.

0
dbush

Il y a en fait IS une différence entre exit(0) et return(0) dans main - lorsque votre fonction main est appelée multiple fois.

Le programme suivant

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

Courir comme

./program 0 0 0 0

Entraînera la sortie suivante:

00000

Cependant celui-ci:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

N'imprimera rien, quels que soient les arguments.

Si vous êtes sûr que personne n'appellera explicitement votre main, ce n'est pas techniquement une grande différence en général, mais il serait beaucoup mieux de maintenir un code plus clair exit. Si, pour une raison quelconque, vous souhaitez appeler main, vous devez l’ajuster à vos besoins.

En parlant de C.

0
radrow