C'è qualche differenza tra return n
(nella funzione main
) e exit(n)
in C? È definito dagli standard C o POSIX o dipende dal sistema operativo o dal compilatore?
C'è qualche differenza tra return n
(nella funzione main
) e exit(n)
in C? È definito dagli standard C o POSIX o dipende dal sistema operativo o dal compilatore?
Nella maggior parte dei casi, non c'è alcuna differenza, ma ecco un programma C che probabilmente si comporta in modo diverso a seconda che utilizzi return 0;
o 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
}
A causa della chiamata di atexit()
, exit(0);
o return 0;
fa invocare la funzione cleanup
. La differenza è che se il programma chiama exit(0);
, la pulizia avviene mentre la "chiamata" a main()
è ancora attiva, quindi l'oggetto local_message
esiste ancora. L'esecuzione di return 0;
, tuttavia, termina immediatamente l'invocazione di main()
e allora richiama la funzione cleanup()
. Poiché cleanup()
fa riferimento (tramite il puntatore globale message
) a un oggetto assegnato localmente a main
e quell'oggetto non esiste più, il comportamento non è definito.
Ecco il comportamento che vedo sul mio sistema:
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
L'esecuzione del programma senza -DUSE_EXIT
può fare qualsiasi cosa, incluso arresto anomalo o stampa di "hello, world"
(se la memoria utilizzata da local_message
non si verifica in modo errato).
In pratica, tuttavia, questa differenza viene visualizzata solo se gli oggetti definiti localmente all'interno di main()
sono resi visibili all'esterno di main()
salvando puntatori su di essi. Ciò potrebbe accadere plausibilmente per argv
. (L'esperimento sul mio sistema mostra che gli oggetti puntati da argv
e da *argv
continuano ad esistere dopo essere tornati da main()
, ma non dovresti dipendere da quello.)
Per C Lo standard dice che un ritorno dalla chiamata iniziale a main equivale a chiamare exit. Tuttavia, non ci si può aspettare che un ritorno da main funzioni se i dati locali al main potrebbero essere necessari durante la pulizia.
Per C ++
Quando exit (0) viene utilizzato per uscire dal programma, i distruttori per oggetti non statici con scope locale non vengono chiamati. Ma i distruttori vengono chiamati se viene usato il valore 0.
Programma 1 - - utilizza exit (0) per uscire
#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);
}
Output: Costruttore di Inside Test
Programma 2 - utilizza il ritorno 0 per uscire
#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;
}
Output:
Costruttore di Inside Test
Inside Test's Destructor
Chiamare i distruttori a volte è importante, ad esempio se il distruttore ha il codice per rilasciare risorse come chiudere i file.
Si noti che gli oggetti statici verranno eliminati anche se chiamiamo exit (). Ad esempio, vedi il seguente programma.
#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);
}
Output:
Costruttore di Inside Test
Inside Test's Destructor
Vale la pena notare che lo standard C (C99) definisce due tipi di ambienti di esecuzione, Ambiente indipendente e Ambiente ospitato . L'ambiente indipendente è un ambiente C che non supporta le librerie C ed è destinato ad applicazioni incorporate e simili. Un ambiente C che supporta le librerie C è chiamato ambiente ospitato.
C99 dice che in un ambiente Freestanding la terminazione del programma è definita dall'implementazione. Pertanto, se l'implementazione definisce main
, return n
e exit
, i loro comportamenti sono come definiti in tale implementazione.
C99 definisce il comportamento dell'ambiente ospitato come,
If the return type of the main function is a type compatible with it, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument; reaching the } that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified.
Dal punto di vista dello standard C, non proprio, tranne che return
è un'istruzione e exit()
è una funzione. O farà sì che tutte le funzioni registrate con atexit()
vengano chiamate seguite dalla terminazione del programma.
Ci sono un paio di situazioni alle quali vuoi prestare attenzione:
main()
. Sebbene raramente visto in pratica, è legale in C. (C ++ lo proibisce esplicitamente.) main()
. A volte un main()
esistente verrà rinominato qualcos'altro e verrà chiamato da un nuovo main()
. L'uso di exit()
introdurrà un bug se uno di questi accade dopo aver scritto il codice, specialmente se non si termina in modo anomalo. Per evitare ciò, è una buona idea prendere l'abitudine di trattare main()
come funzione e usare return
quando vuoi che termini.