C'è qualche differenza tra return n
(nella main
funzione) 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 main
funzione) e exit(n)
in C? È definito dagli standard C o POSIX o dipende dal sistema operativo o dal compilatore?
Risposte:
Nella maggior parte dei casi, non c'è alcuna differenza, ma ecco un programma C che probabilmente si comporterà 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 atexit()
chiamata, una exit(0);
o return 0;
fa cleanup
invocare la funzione. La differenza è che se il programma chiama exit(0);
, la pulizia avviene mentre la "chiamata" main()
è ancora attiva, quindi l' local_message
oggetto esiste ancora. L'esecuzione return 0;
, tuttavia, termina immediatamente l'invocazione di main()
e quindi richiama la cleanup()
funzione. Poiché si cleanup()
riferisce (tramite il message
puntatore globale ) a un oggetto allocato localmente main
e tale 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
potrebbe fare nulla, incluso l'arresto anomalo o la stampa "hello, world"
(se la memoria utilizzata da local_message
non risulta bloccata).
In pratica, tuttavia, questa differenza viene visualizzata solo se gli oggetti definiti localmente all'interno main()
vengono resi visibili all'esterno main()
salvando i puntatori su di essi. Questo potrebbe plausibilmente accadere per argv
. (L'esperimento sul mio sistema mostra che gli oggetti indicati da argv
e da *argv
continuano ad esistere dopo il ritorno da main()
, ma non dovresti dipendere da quello.)
Per C
Lo standard afferma che un ritorno dalla chiamata iniziale alla principale equivale alla chiamata di uscita. Tuttavia, non ci si può aspettare che un ritorno da main funzioni se i dati locali a 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 ambito locale non vengono chiamati. Ma i distruttori vengono chiamati se si utilizza return 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 del test interno
Programma 2: utilizza return 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: Inside Test's Costruttore
Inside Test's Destructor
Talvolta è importante chiamare i distruttori, ad esempio se distruttore ha un codice per rilasciare risorse come la chiusura dei file.
Nota che gli oggetti statici verranno ripuliti anche se chiamiamo exit (). Ad esempio, vedere 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: Inside Test's Costruttore
Inside Test's Destructor
finally
Vale la pena notare che lo standard C (C99) definisce due tipi di ambienti di esecuzione, l' ambiente indipendente e l' ambiente ospitato . L'ambiente indipendente è un ambiente C che non supporta le librerie C ed è destinato ad applicazioni integrate e simili. L'ambiente CA che supporta le librerie C è chiamato ambiente ospitato.
C99 afferma che in un ambiente indipendente la terminazione del programma è definita dall'implementazione. Quindi, se l'implementazione definisce main
, return n
e exit
, i loro comportamenti sono come definiti in quella implementazione.
C99 definisce il comportamento dell'ambiente ospitato come,
Se il tipo restituito della funzione principale è compatibile con esso, un ritorno dalla chiamata iniziale alla funzione principale equivale a chiamare la funzione di uscita con il valore restituito dalla funzione principale come argomento; il raggiungimento di} che termina la funzione principale restituisce un valore di 0. Se il tipo restituito non è compatibile con int, lo stato di terminazione restituito all'ambiente host non è specificato.
Dal punto di vista dello standard C, non proprio, oltre ad return
essere un'affermazione ed exit()
essere una funzione. Entrambe causeranno la atexit()
chiamata di tutte le funzioni registrate seguite dalla conclusione del programma.
Ci sono un paio di situazioni a cui vuoi fare attenzione:
main()
. Sebbene raramente visto in pratica, è legale in C. (C ++ lo proibisce esplicitamente.)main()
. A volte un esistente main()
verrà rinominato qualcos'altro e sarà chiamato da un nuovo main()
.L'uso di exit()
introdurrà un bug se uno di questi si verifica dopo aver scritto il codice, specialmente se non termina in modo anomalo. Per evitarlo, è una buona idea avere l'abitudine di trattare main()
come la funzione che è e usare return
quando vuoi che finisca.