Czy jest jakaś różnica między return n
(w main
funkcji) a exit(n)
w C? Czy jest zdefiniowany przez standardy C lub POSIX, czy zależy od systemu operacyjnego lub kompilatora?
W większości przypadków nie ma różnicy, ale oto program C, który prawdopodobnie zachowuje się inaczej w zależności od tego, czy używa return 0;
lub 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
}
Ze względu na atexit()
rozmowy, albo exit(0);
czy return 0;
spowoduje, że cleanup
funkcja się powoływać. Różnica polega na tym, że jeśli program wywołuje exit(0);
, czyszczenie odbywa się, gdy „wywołanie” main()
jest nadal aktywne, więc local_message
obiekt nadal istnieje. Wykonanie powoduje return 0;
jednak natychmiastowe zakończenie wywołania, main()
a następnie wywołanie cleanup()
funkcji. Ponieważ cleanup()
odnosi się (za pomocą message
wskaźnika globalnego ) do obiektu przydzielonego lokalnie main
i obiekt ten już nie istnieje, zachowanie jest niezdefiniowane.
Oto zachowanie, które widzę w moim systemie:
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
Uruchomienie programu bez -DUSE_EXIT
niczego może zrobić, włączając awarię lub drukowanie "hello, world"
(jeśli local_message
zdarza się, że używana pamięć nie jest blokowana).
W praktyce jednak ta różnica pojawia się tylko wtedy, gdy obiekty zdefiniowane lokalnie wewnątrz main()
są widoczne na zewnątrz main()
poprzez zapisanie do nich wskaźników. Możliwe, że tak się stanie argv
. (Eksperyment w moim systemie pokazuje, że obiekty wskazywane przez argv
i przez *argv
istnieją po powrocie main()
, ale nie powinieneś na tym polegać).
Dla C
Standard mówi, że powrót z pierwszego połączenia do głównego jest równoważny z wywołaniem wyjścia. Jednak nie można oczekiwać powrotu z main, jeśli dane lokalne do main mogą być potrzebne podczas czyszczenia.
Dla C ++
Kiedy exit (0) jest używane do wyjścia z programu, niszczyciele dla obiektów niestacjonarnych o zasięgu lokalnym nie są wywoływane. Ale destruktory są wywoływane, jeśli użyty jest return 0.
Program 1 - - używa wyjścia (0) do wyjścia
#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);
}
Wyjście: konstruktor testu wewnętrznego
Program 2 - używa return 0, aby wyjść
#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;
}
Dane wyjściowe: Konstruktor
testu wewnętrznego Inside Destructor testu
Wywoływanie destruktorów jest czasem ważne, na przykład, jeśli destruktor ma kod do zwalniania zasobów takich jak zamykanie plików.
Zauważ, że obiekty statyczne zostaną wyczyszczone, nawet jeśli wywołamy exit (). Na przykład patrz następujący program.
#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);
}
Dane wyjściowe: Konstruktor
testu wewnętrznego Inside Destructor testu
finally
Warto zauważyć, że standard C (C99) definiuje dwa typy środowisk wykonawczych: środowisko wolnostojące i środowisko hostowane . Środowisko wolnostojące to środowisko C, które nie obsługuje bibliotek C i jest przeznaczone dla aplikacji osadzonych i tym podobnych. Środowisko AC obsługujące biblioteki C nazywa się środowiskiem hostowanym.
C99 mówi, że w środowisku wolnostojącym zakończenie programu jest zdefiniowane jako implementacja. Zatem jeśli implementacja definiuje
main
,return n
aexit
ich zachowania są takie, jak zdefiniowano w tej implementacji.C99 definiuje zachowanie środowiska hostowanego jako,
źródło
Z punktu widzenia standardu C, nie do końca, poza
return
byciem wyrażeniem iexit()
funkcją. Albo spowoduje wywołanie wszystkich zarejestrowanych funkcji,atexit()
po których nastąpi zakończenie programu.Istnieje kilka sytuacji, na które należy uważać:
main()
. Choć rzadko spotykany w praktyce, jest legalny w C. (C ++ wyraźnie tego zabrania).main()
. Czasami istniejącymain()
zostanie przemianowany na coś innego i zostanie nazwany przez nowegomain()
.Użycie
exit()
spowoduje wprowadzenie błędu, jeśli którykolwiek z nich wystąpi po napisaniu kodu, zwłaszcza jeśli nie zakończy się on nieprawidłowo. Aby tego uniknąć, warto mieć w zwyczaju traktowaćmain()
tę funkcję, jaką jest, i używać,return
kiedy chcesz, aby się zakończyła.źródło