3

Hi I wanted to know what the differences between returning and exiting from the main function are. What happens behind the scenes when each of them are invoked, and how is the control returned in each case. I would really appreaciate it if someone could dive deep into this subject.

FRANCISCO BERNAD
  • 473
  • 4
  • 15
  • The point of `exit()` is that you can call it from nested functions not just from `main`. Functionally they are equivalent. – Jester Aug 07 '20 at 22:02
  • The details of what the C library does when you call exit, or when you return to the CRT start code that called `main`, obviously depends on the OS, and on the C library. (In general terms it of course has to implement all the ISO C requirements like flushing stdio buffers, and running atexit functions). I think normally `main`'s caller will do something equivalent to `exit(main(argc, argv))` though, so either way the same stuff happens. If you're looking for asm details on one specific platform, say which one in your question. (Or single-step asm instructions out of main). – Peter Cordes Aug 07 '20 at 22:02
  • 2
    Does this answer your question? [What is the difference between exit and return?](https://stackoverflow.com/questions/3463551/what-is-the-difference-between-exit-and-return) –  Aug 07 '20 at 22:07
  • 1
    As part of understanding CRT startup code in general, for GNU/Linux see [Linux x86 Program Start Up or - How the heck do we get to main()?](http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html). It mentions that `__libc_start_main` calls `exit` with main's return value. With the question being not specific to any group of C implementations, I can't really post this as an answer, though. You're not going to get anything more specific than rici's answer about how implementations usually do it. – Peter Cordes Aug 07 '20 at 23:10

4 Answers4

9

There is no difference.

Behind the scenes, what happens (at least on some popular operating systems) is this:

// Set up argc and argv
int retcode = main(argc, argv);
exit(retcode);

That behaviour is guaranteed by the C standard:

... a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument ... (§5.1.2.2.3)

rici
  • 234,347
  • 28
  • 237
  • 341
  • It may be worth noting that if a C99 implementation processes programs in such fashion, it would be required to add a `return 0;` to the end of the function called `main()` unless such a statement would be unreachable. – supercat Aug 07 '20 at 22:09
  • 3
    @supercat: That case is covered in the same paragraph of the standard which I quoted. But, sure. – rici Aug 07 '20 at 22:11
  • 1
    I was really trying to avoid the [TMI syndrome](https://en.wiktionary.org/wiki/too_much_information) here, but as @acorn notes, there's one difference: if you return, then the lifetime of automatic objects will have ended. That makes a difference if you have used `atexit()` to cause some function to trigger on exit and that function refers to a static variable which contained a pointer to some automatic object. It hardly seems worth warning "Don't do that". You can see this effect: add an extra run-time argument to this [example code](http://coliru.stacked-crooked.com/a/634bb0b24307eb69). – rici Aug 07 '20 at 22:39
  • One example of the details of an implementation that does exactly this in its CRT + libc: See [Linux x86 Program Start Up or - How the heck do we get to main()?](http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html) - `__libc_start_main` gets a function-pointer to main, which it uses and then calls `exit` with the return value. – Peter Cordes Aug 07 '20 at 23:13
  • @PeterCordes: That was one of the popular operating systems I was referring to :-) – rici Aug 07 '20 at 23:20
  • I think this question is making me crazy because it seems somewhat reasonable at face value, but then it's asking for asm-level details without any info on which platform it wants to know about. So I feel like if it's going to get answered at all instead of just downvoted, it needs at least some specific example with some low-level details. (including something about what `exit` does? cleanup and then syscall...) I like your answer, though; good balance of as much as we can say about the practical implementation, with the ISO standard to back up why it has to be at least equivalent. – Peter Cordes Aug 07 '20 at 23:29
  • Beside behavior of local variables, another difference between exit() and returning from main() is that it is allowed to recursively call main(), henceforth the behavior of ending the program will only happens when returning from the initial call to main. Any other return from main, will go back wherever it came from. – kriss Aug 09 '20 at 11:54
4

Beyond other answers saying the two approaches are virtually the same:

From the assembly programmer's point of view, the primary difference when using exit() is that we don't have to (save or) restore the callee-saves registers, or the return address (if the ABI puts that in a register, that is).

If main returns to its caller then main ought to honor the ABI's register preservation requirements.

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53
3

There is basically no difference. However, exit() is useful because it allows you to exit the program from other functions different than main().

The only formal difference is that:

...the lifetimes of objects with automatic storage duration declared in main will have ended...

in the case of returning from main.


If you need more details, I suggest you read the latest C standard, in particular section 5.1.2.2.3:

...a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument...

As well as section 7.22.4.4:

The exit function causes normal program termination to occur...

Note that there are also other ways to exit a program, like abort, quick_exit and _Exit.

Acorn
  • 24,970
  • 5
  • 40
  • 69
2

C 2018 5.1.2.2.3 1 tells us what happens in a hosted environment:

If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument; reaching the } that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified.

So, in a hosted environment, what you likely think of as the “normal” C environment, return x; from the initial call to main is equivalent to exit(x);, if it was declared with a return type compatible with int. (C implementations may define other allowed declarations.)

In a freestanding environment, 5.1.2.1 2 tells us:

The effect of program termination in a freestanding environment is implementation-defined.

The behavior of exit is specified in 7.22.4.4:

3 First, all functions registered by the atexit function are called, in the reverse order of their registration, except that a function is called after any previously registered functions that had already been called at the time it was registered…

4 Next, all open streams with unwritten buffered data are flushed, all open streams are closed, and all files created by the tmpfile function are removed.

5 Finally, control is returned to the host environment. If the value of status [the parameter of exit] is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned. If the value of status is EXIT_FAILURE, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312