Y a-t-il une différence entre le retour n et la sortie (n) en C?


9

Y a-t-il une différence entre return n(dans la mainfonction) 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:


5

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 cleanupfonction. 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_messageobjet 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 messagepointeur global ) à un objet auquel est affecté localement mainet 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_EXITpourrait faire quoi que ce soit, y compris planter ou imprimer "hello, world"(si la mémoire utilisée par local_messagene 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 argvet par *argvcontinuent d'exister après le retour de main(), mais vous ne devriez pas en dépendre.)


16
  • 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


La fermeture de fichiers n'est pas vraiment un bon exemple d'un destructeur important à tirer lorsque vous quittez, car les fichiers seront de toute façon fermés à la sortie du programme.
Winston Ewert

1
@WinstonEwert: Vrai, mais il peut exister des tampons au niveau de l'application qui doivent encore être vidés.
Philipp

1
La question ne mentionne C ++ nulle part ...
tdammers

Je ne connais aucun des deux langages, alors pardonnez-moi, mais cette réponse me fait penser que la sortie est comme le failfast de C #, donc en C ++, la sortie dans un essai exécute-t-elle finalement un?
Jimmy Hoffa

@JimmyHoffa, c ++ n'a pasfinally
Winston Ewert

6

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 net 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é.


1
Et cela n'a vraiment aucun sens d'appeler exit () à partir d'un environnement autonome. L'équivalent intégré de exit () serait de bloquer le programme dans une boucle éternelle, puis d'attendre que le chien de garde expire.

0

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:

  • Récursivité dans main(). Bien que rarement vu dans la pratique, il est légal en C. (C ++ l'interdit explicitement.)
  • Réutilisation de 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 returnquand vous voulez qu'elle se termine.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.