Difference Between return n and exit(n) in C

cstandards

Is there any difference between return n (in the main function) and exit(n) in C? Is it defined by C or POSIX standards or it depends on OS or compiler?

Best Answer

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