In most cases, there's no difference, but here's a C program that's likely to behave differently depending on whether it uses return 0;
or 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
}
Because of the atexit()
call, either exit(0);
or return 0;
causes the cleanup
function to be invoked. The difference is that if the program calls exit(0);
, the cleanup happens while the "call" to main()
is still active, so the local_message
object still exists. Executing return 0;
, however, immediately terminates the invocation of main()
and then invokes the cleanup()
function. Since cleanup()
refers (via the global message
pointer) to an object that's allocated locally to main
, and that object no longer exists, the behavior is undefined.
Here's the behavior I see on my system:
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
Running the program without -DUSE_EXIT
could do anything, including crashing or printing "hello, world"
(if the memory used by local_message
happens not to be clobbered).
In practice, though, this difference only shows up if objects defined locally inside main()
are made visible outside main()
by saving pointers to them. This could plausibly happen for argv
. (Experiment on my system shows that the objects pointed to by argv
and by *argv
continue to exist after returning from main()
, but you shouldn't depend on that.)
Best Answer
I agree with you that it's overly pessimistic, but some (potentially historical) reasons: