Y a-t-il une différence entre return n
(dans la main
fonction) et exit(n)
en C? Est-il défini par les normes C ou POSIX ou cela dépend du système d'exploitation ou du compilateur?
Y a-t-il une différence entre return n
(dans la main
fonction) et exit(n)
en C? Est-il défini par les normes C ou POSIX ou cela dépend du système d'exploitation ou du compilateur?
Réponses:
Dans la plupart des cas, il n'y a pas de différence, mais voici un programme C qui se comportera probablement différemment selon qu'il utilise return 0;
ou exit(0);
:
#include <stdio.h>
#include <stdlib.h>
static char *message;
void cleanup(void) {
printf("message = \"%s\"\n", message);
}
int main(void) {
char local_message[] = "hello, world";
message = local_message;
atexit(cleanup);
#ifdef USE_EXIT
puts("exit(0);");
exit(0);
#else
puts("return 0;");
return 0;
#endif
}
En raison de l' atexit()
appel, soit exit(0);
ou return 0;
provoque l' appel de la cleanup
fonction. La différence est que si le programme appelle exit(0);
, le nettoyage a lieu pendant que "l'appel" à main()
est toujours actif, donc l' local_message
objet existe toujours. L' exécution return 0;
, cependant, se termine immédiatement l'invocation de main()
et puis appelle la cleanup()
fonction. Puisque cleanup()
fait référence (via le message
pointeur global ) à un objet auquel est affecté localement main
et que cet objet n'existe plus, le comportement n'est pas défini.
Voici le comportement que je vois sur mon système:
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
Exécuter le programme sans -DUSE_EXIT
pourrait faire quoi que ce soit, y compris planter ou imprimer "hello, world"
(si la mémoire utilisée par local_message
ne se trouve pas être encombrée).
En pratique, cependant, cette différence n'apparaît que si les objets définis localement à l'intérieur main()
sont rendus visibles à l'extérieur main()
en leur enregistrant des pointeurs. Cela pourrait vraisemblablement arriver pour argv
. (L'expérience sur mon système montre que les objets pointés par argv
et par *argv
continuent d'exister après le retour de main()
, mais vous ne devriez pas en dépendre.)
Pour C,
la norme dit qu'un retour de l'appel initial à main équivaut à appeler exit. Cependant, un retour du principal ne peut pas fonctionner si des données locales au principal peuvent être nécessaires pendant le nettoyage.
Pour C ++
Lorsque exit (0) est utilisé pour quitter le programme, les destructeurs des objets non statiques de portée locale ne sont pas appelés. Mais les destructeurs sont appelés si return 0 est utilisé.
Programme 1 - - utilise exit (0) pour quitter
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
getchar();
}
};
int main() {
Test t1;
// using exit(0) to exit from main
exit(0);
}
Sortie: Constructeur du test intérieur
Programme 2 - utilise le retour 0 pour quitter
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
}
};
int main() {
Test t1;
// using return 0 to exit from main
return 0;
}
Sortie: Constructeur du
test intérieur Destructeur du test intérieur
L'appel de destructeurs est parfois important, par exemple, si le destructeur a du code pour libérer des ressources comme la fermeture de fichiers.
Notez que les objets statiques seront nettoyés même si nous appelons exit (). Par exemple, voir le programme suivant.
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
getchar();
}
};
int main() {
static Test t1; // Note that t1 is static
exit(0);
}
Sortie: Constructeur du
test intérieur Destructeur du test intérieur
finally
Il convient de noter que la norme C (C99) définit deux types d'environnements d'exécution, l'environnement autonome et l' environnement hébergé . L'environnement autonome est un environnement C qui ne prend pas en charge les bibliothèques C et est destiné aux applications intégrées et similaires. L'environnement AC qui prend en charge les bibliothèques C est appelé environnement hébergé.
C99 indique que, dans un environnement autonome, la fin du programme est définie par l'implémentation. Donc, si les définit de mise en œuvre main
, return n
et exit
, leurs comportements sont comme est défini dans cette mise en œuvre.
C99 définit le comportement de l'environnement hébergé comme,
Si le type de retour de la fonction principale est compatible avec lui, un retour de l'appel initial à la fonction principale équivaut à appeler la fonction exit avec la valeur renvoyée par la fonction principale comme argument; atteindre le} qui termine la fonction principale renvoie une valeur de 0. Si le type de retour n'est pas compatible avec int, l'état de terminaison renvoyé à l'environnement hôte n'est pas spécifié.
Du point de vue de la norme C, pas vraiment, à part return
être une déclaration et exit()
une fonction. L'une ou l'autre entraînera l' atexit()
appel des fonctions enregistrées avec suivi de la fin du programme.
Il y a quelques situations à surveiller:
main()
. Bien que rarement vu dans la pratique, il est légal en C. (C ++ l'interdit explicitement.)main()
. Parfois, un existant main()
sera renommé quelque chose d'autre et sera appelé par un nouveau main()
.L'utilisation de exit()
introduira un bogue si l'un de ces cas survient après avoir écrit le code, surtout s'il ne se termine pas anormalement. Pour éviter cela, c'est une bonne idée de prendre l'habitude de traiter main()
comme la fonction qu'elle est et d'utiliser return
quand vous voulez qu'elle se termine.