Existe alguma diferença entre return n
(na main
função) e exit(n)
em C? É definido pelos padrões C ou POSIX ou depende do SO ou do compilador?
Existe alguma diferença entre return n
(na main
função) e exit(n)
em C? É definido pelos padrões C ou POSIX ou depende do SO ou do compilador?
Respostas:
Na maioria dos casos, não há diferença, mas aqui está um programa em C que provavelmente se comportará de maneira diferente dependendo de usar 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
}
Por causa da atexit()
chamada, exit(0);
ou return 0;
faz com que a cleanup
função seja chamada. A diferença é que, se o programa chama exit(0);
, a limpeza acontece enquanto a "chamada" main()
ainda está ativa, portanto o local_message
objeto ainda existe. A execução return 0;
, no entanto, termina imediatamente a chamada main()
e depois chama a cleanup()
função. Como cleanup()
se refere (por meio do message
ponteiro global ) a um objeto alocado localmente main
e esse objeto não existe mais, o comportamento é indefinido.
Aqui está o comportamento que vejo no meu sistema:
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
A execução do programa sem -DUSE_EXIT
pode fazer nada, incluindo travar ou imprimir "hello, world"
(se a memória usada por local_message
não for derrotada).
Na prática, porém, essa diferença só aparece se os objetos definidos localmente dentro main()
são tornados visíveis fora main()
, salvando ponteiros para eles. Isso poderia acontecer plausivelmente argv
. (A experiência no meu sistema mostra que os objetos apontados por argv
e por *argv
continuam existindo após o retorno main()
, mas você não deve depender disso.)
Para C
O Standard diz que um retorno da chamada inicial para a principal é equivalente a chamar exit. No entanto, não se espera que um retorno do main funcione se forem necessários dados locais para o main durante a limpeza.
Para C ++
Quando exit (0) é usado para sair do programa, os destruidores de objetos não estáticos com escopo local não são chamados. Mas os destruidores são chamados se o retorno 0 for usado.
Programa 1 - - usa exit (0) para sair
#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);
}
Saída: Construtor do Teste Interno
Programa 2 - usa o retorno 0 para sair
#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;
}
Saída: Construtor do
Inside Test Destrutor do Inside Test
Chamar destruidores às vezes é importante, por exemplo, se o destruidor tiver código para liberar recursos, como fechar arquivos.
Observe que os objetos estáticos serão limpos mesmo se chamarmos exit (). Por exemplo, consulte o programa a seguir.
#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);
}
Saída: Construtor do
Inside Test Destrutor do Inside Test
finally
Vale ressaltar que o padrão C (C99) define dois tipos de ambientes de execução, Ambiente Independente e Ambiente Hospedado . O ambiente independente é um ambiente C que não suporta as bibliotecas C e destina-se a aplicativos incorporados e similares. O ambiente de CA que suporta as bibliotecas C é chamado de ambiente hospedado.
C99 diz que, em um ambiente independente, a finalização do programa é definida como implementação. Então, se os define de implementação main
, return n
e exit
, seus comportamentos são como é definida em que a implementação.
C99 define o comportamento do ambiente hospedado como,
Se o tipo de retorno da função principal for compatível, um retorno da chamada inicial para a função principal será equivalente a chamar a função de saída com o valor retornado pela função principal como argumento; atingir o} que finaliza a função principal retorna um valor 0. Se o tipo de retorno não for compatível com int, o status de finalização retornado ao ambiente host não será especificado.
Do ponto de vista do padrão C, não realmente, além de return
ser uma declaração e exit()
uma função. Qualquer um deles fará com que quaisquer funções registradas atexit()
sejam chamadas, seguidas pelo término do programa.
Há algumas situações que você deseja observar:
main()
. Embora raramente seja visto na prática, é legal em C. (o C ++ proíbe explicitamente).main()
. Às vezes, um existente main()
será renomeado para outra coisa e será chamado por um novo main()
.O uso de exit()
introduzirá um bug se um desses acontecer depois que você escrever o código, especialmente se não terminar de forma anormal. Para evitar isso, é uma boa idéia ter o hábito de tratar main()
como a função e usá-la return
quando quiser que ela termine.