C'è qualche differenza tra il ritorno n e l'uscita (n) in C?


9

C'è qualche differenza tra return n(nella mainfunzione) e exit(n)in C? È definito dagli standard C o POSIX o dipende dal sistema operativo o dal compilatore?

Risposte:


5

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 cleanupinvocare la funzione. La differenza è che se il programma chiama exit(0);, la pulizia avviene mentre la "chiamata" main()è ancora attiva, quindi l' local_messageoggetto 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 messagepuntatore globale ) a un oggetto allocato localmente maine 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_EXITpotrebbe fare nulla, incluso l'arresto anomalo o la stampa "hello, world"(se la memoria utilizzata da local_messagenon 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 argve da *argvcontinuano ad esistere dopo il ritorno da main(), ma non dovresti dipendere da quello.)


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


La chiusura dei file non è in realtà un buon esempio di un importante distruttore da sparare quando si esce, perché i file verranno comunque chiusi all'uscita dal programma.
Winston Ewert,

1
@WinstonEwert: True, ma potrebbero esistere buffer a livello di applicazione che devono ancora essere scaricati.
Philipp,

1
La domanda non menziona C ++ da nessuna parte ...
tdammers,

Non conosco nessuno dei due linguaggi per cui perdonami, ma questa risposta mi fa pensare che exit sia come il failfast di C #, quindi in C ++ exit in un tentativo esegue finalmente un?
Jimmy Hoffa,

@JimmyHoffa, c ++ non hafinally
Winston Ewert,

6

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 ne 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.


1
E non ha davvero senso chiamare exit () da un ambiente indipendente. L'equivalente incorporato di exit () sarebbe appendere il programma in un ciclo eterno, quindi attendere il timeout del watchdog.

0

Dal punto di vista dello standard C, non proprio, oltre ad returnessere 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:

  • Ricorsione in main(). Sebbene raramente visto in pratica, è legale in C. (C ++ lo proibisce esplicitamente.)
  • Riutilizzo di 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 returnquando vuoi che finisca.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.