déclaration de retour vs exit () dans main ()
-
19-08-2019 - |
Question
Devrais-je utiliser les instructions exit ()
ou simplement renvoyer
dans main ()
? Personnellement, je préfère les instructions return
car j’ai le sentiment que c’est comme si vous lisiez 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 renvoie
ne fait pas?
La solution
En fait, il y a une différence, mais elle 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 dimensionné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 j’appelle cela, il n’ya pas de "backsies". Tous les objets que vous avez créés dans cette fonction ne seront pas détruits. Souvent, cela n’a aucune implication, 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'aura son destructeur appelé.
Faites preuve de prudence lorsque vous privilégiez la sortie au retour.
http://groups.google.com/group/gnu. gcc.help/msg/8348c50030cfd15a
Autres conseils
Une autre différence:
exit
est une bibliothèque standard
fonction si vous avez besoin d'inclure
en-têtes et lien avec la norme
bibliothèque. Pour illustrer (en C ++),
c'est 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
à partir de main
a les mêmes effets secondaires que celui 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
). Codez-vous une application qui utilise le C-runtime? Un plugin Maya? Un service Windows? Un conducteur? Chaque cas nécessite des recherches pour voir si exit
est équivalent à return
. IMHO utilise exit
lorsque vous entendez vraiment renvoyer
rend le code encore plus confus. OTOH, si vous voulez vraiment dire exit
, alors utilisez-le.
Il y a au moins une raison pour 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 attribuer à l'un des flux standard un tampon de durée de stockage automatique dans main
, puis en revenant de main
produit un comportement non défini, mais l'appel de exit
est valide.
Une autre utilisation potentielle (généralement réservée aux programmes de jouets) est de quitter un programme avec des invocations récursives de main
.
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'y a pas d'instruction return
explicite. É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
Je FORTEMENT d'appuyer le commentaire de R. sur l'utilisation de exit () afin d'éviter d'avoir la mémoire automatique stockée dans main ()
avant que le programme ne se termine réellement. Une instruction return X;
dans main ()
n'est pas précisément équivalente à un appel à exit (X);
, car le stockage dynamique de < code> main () disparaît lorsque main ()
revient, mais 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 généralement techniquement vraie si vous comptez la routine de démarrage C qui a appelé votre fonction main ()
, ce n'est pas exactement ce que vous voulez dire quand vous voulez mettre fin au processus.
Après tout, si vous souhaitez terminer votre programme depuis une autre fonction que main ()
, vous devez appeler exit ()
. Faire cela de manière cohérente dans main ()
rend également votre code beaucoup plus lisible, et facilite beaucoup la redéfinition du code par quiconque. C'est-à-dire que le code copié de main ()
vers une autre fonction ne se comportera pas mal en raison d'instructions accidentelles return
que devrait avoir été exit ( )
appelle.
Donc, en combinant tous ces points, la conclusion est que c’est une mauvaise habitude , au moins pour C, d’utiliser une instruction return
pour terminer le programme dans < code> main () .
Est-ce que exit () fait quelque chose de spécial que 'return' ne fait pas?
Avec certains compilateurs pour plates-formes inhabituelles, exit ()
peut traduire son argument dans la valeur de sortie de votre programme, tandis qu'un retour de main ()
pourrait simplement passer directement la valeur à l'environnement hôte sans traduction.
La norme requiert un comportement identique dans ces cas (en particulier, elle indique que le renvoi de quelque chose qui est int
- compatible avec main ()
doit ê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 beaucoup de systèmes (BEAUCOUP!), 0 signifie succès et toute autre chose 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 message désagréable 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
en tant qu'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
en 0
.
Par conséquent, le standard vous lie à VMS, car il ne laisse aucun moyen standard de renvoyer un code échec qui a la valeur 0.
Le compilateur C VAX / VMS C du début des années 90 n’a donc pas interprété la valeur renvoyée par main ()
, il a simplement renvoyé la valeur de l’environnement hôte. Mais si vous utilisiez exit ()
, il répondrait aux exigences de la norme: traduire EXIT_SUCCESS
(ou 0
) en code de succès et . EXIT_FAILURE
en un code d’erreur générique. Pour utiliser EXIT_SUCCESS
, vous deviez le transmettre à exit ()
, vous ne pouviez pas le renvoyer depuis 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 */
}
De côté: si je me souviens bien, la convention VMS pour les valeurs de sortie est plus nuancée que celle de pair / impair En fait, il utilise quelque chose comme les trois bits inférieurs pour coder un niveau de gravité. De manière générale, toutefois, les niveaux de gravité impairs indiquaient le succès ou des informations diverses et les erreurs paires indiquaient des erreurs.
En C, le retour de main
est identique à l'appel de exit
avec la même valeur.
Section 5.1.2.2.3 du C norme déclare:
Si le type de retour de la fonction principale est un type compatible avec int , un retour de l'appel initial à la fonction principale équivaut à appeler la fonction de sortie avec la valeur renvoyée par le principal fonctionne comme son argument ; 11) atteindre le} qui termine le fonction principale renvoie la valeur 0. Si le type de retour est non compatible avec int, le statut de fin est renvoyé à la l'environnement hôte n'est pas spécifié.
Les règles pour C ++ sont un peu différentes, comme indiqué dans d'autres réponses.
Il existe en réalité une différence entre exit (0)
et return (0)
dans main
& # 8211; & nbsp; lorsque votre < La fonction code> main est appelée plusieurs 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));
}
Exécuter en tant que
./program 0 0 0 0
Le résultat sera le suivant:
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 certain que personne n'appellera explicitement votre main
, ce n'est pas techniquement une grande différence en général, mais maintenir un code plus clair exit
serait beaucoup mieux. Si, pour une raison quelconque, vous souhaitez appeler main
& # 8211; vous devez l’adapter à vos besoins.
Parler de C.