Existe alguma diferença entre retorno n e saída (n) em C?

9

Existe alguma diferença entre return n(na mainfunção) e exit(n)em C? É definido pelos padrões C ou POSIX ou depende do SO ou do compilador?

Thomas Owens
fonte

Respostas:

5

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 cleanupfunçã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_messageobjeto 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 messageponteiro global ) a um objeto alocado localmente maine 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_EXITpode fazer nada, incluindo travar ou imprimir "hello, world"(se a memória usada por local_messagenã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 argve por *argvcontinuam existindo após o retorno main(), mas você não deve depender disso.)

Keith Thompson
fonte
16
  • 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

Vaibhav Agarwal
fonte
Fechar arquivos não é realmente um bom exemplo de um destruidor importante a ser disparado quando você sai, porque os arquivos serão fechados na saída do programa de qualquer maneira.
Winston Ewert
11
@WinstonEwert: Verdade, mas pode haver buffers no nível do aplicativo que ainda precisam ser liberados.
28512 Philipp Philipp
11
A questão não menciona C ++ em qualquer lugar ...
tdammers
Eu não conheço nenhuma linguagem, então me perdoe, mas essa resposta me faz pensar que a saída é como a falha do C #, então no C ++ a saída em uma tentativa executa um finalmente?
21712 Jimmy Jimmy -offoff
@JimmyHoffa, c ++ não temfinally
Winston Ewert
6

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

theD
fonte
11
E realmente não faz sentido chamar exit () de um ambiente independente. O equivalente incorporado de exit () seria interromper o programa em um loop eterno e aguardar o tempo limite do watchdog.
0

Do ponto de vista do padrão C, não realmente, além de returnser 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:

  • Recursão em main(). Embora raramente seja visto na prática, é legal em C. (o C ++ proíbe explicitamente).
  • Reutilização de 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 returnquando quiser que ela termine.

Blrfl
fonte