207

Should I use exit() or just return statements in main()? Personally I favor the return statements because I feel it's like reading any other function and the flow control when I'm reading the code is smooth (in my opinion). And even if I want to refactor the main() function, having return seems like a better choice than exit().

Does exit() do anything special that return doesn't?

Bugfinger
  • 1,198
  • 1
  • 14
  • 29
Srikanth
  • 11,148
  • 20
  • 64
  • 87

8 Answers8

289

Actually, there is a difference, but it's subtle. It has more implications for C++, but the differences are important.

When I call return in main(), destructors will be called for my locally scoped objects. If I call exit(), no destructor will be called for my locally scoped objects! Re-read that. exit() does not return. That means that once I call it, there are "no backsies." Any objects that you've created in that function will not be destroyed. Often this has no implications, but sometimes it does, like closing files (surely you want all your data flushed to disk?).

Note that static objects will be cleaned up even if you call exit(). Finally note, that if you use abort(), no objects will be destroyed. That is, no global objects, no static objects and no local objects will have their destructors called.

Proceed with caution when favoring exit over return.

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a

FreeMemory
  • 8,142
  • 7
  • 32
  • 48
  • 1
    abort() exits with error condition (non-zero exit code) and may be even a core. If you need to exit w/o calling static destructors, use _exit . –  Jan 20 '09 at 14:46
  • @Arkadiy **Absolutely.** abort signals some error condition. And, stylistically, I usually also use exit() to signal an error condition, and return 0 from main to indicate normal termination. – FreeMemory Jan 20 '09 at 14:48
  • 1
    From the exit() manpage: 2. Flush all open output streams. It sounds like any open output streams would be flushes regardless of whether the appropriate destructor is called. – Michael Koval Jan 19 '10 at 05:47
  • 7
    @Mike: there's a difference between the C library file buffers and the C++ file stream objects. exit() - being part of the C library - was designed to coordinate with and flush the former, but can bypass the latter: even Standard C++ fstream content is not flushed to disk (try it - I did, it fails w/ Linux/GCC), and obviously user-defined types that have buffered I/O can't be expected to flush either. – Tony Delroy Nov 16 '10 at 01:56
  • 9
    Note: The statement: **no destructor will be called for my locally scoped objects!** is no longer true for C++11: - Objects associated with the current thread with thread storage duration are destroyed (C++11 only). http://www.cplusplus.com/reference/cstdlib/exit/ – Ilendir Aug 11 '14 at 13:25
  • 7
    It means, `thread_local` objects' destructors will be called. Destructors for other local objects are still not called. http://ideone.com/Y6Dh3f – HolyBlackCat Dec 04 '14 at 18:23
  • 3
    BTW, and just to be pedantic and because this answer can still be confusing for readers using C: For C the issue about `exit()` closing files cleanly is actually wrong. The only time data might not be flushed is in the opposite case: i.e. if one uses `return` from `main()` and one has called `setbuf()` or `setvbuf()` with a buffer declared as automatic storage in `main()` (as discussed in R.'s answer below). It's really too bad this question is tagged with both C and C++ (and coding-style -- it's not a style issue!). – Greg A. Woods Sep 03 '15 at 21:27
  • 1
    This answer should be updated to take comments and other answers into account. In it's current state it's incorrect and having (for instance it's mixing up flushing and close, and C io are flushed, while C++ fstreams aren't). Having it as the accepted answer is annoying. I will downvote until it is fixed. – kriss Jun 06 '16 at 22:45
25

Another difference: exit is a Standard Library function so you need to include headers and link with the standard library. To illustrate (in C++), this is a valid program:

int main() { return 0; }

but to use exit you'll need an include:

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

Plus this adds an additional assumption: that calling exit from main has the same side effects as returning zero. As others have pointed out, this depends on what kind of executable you're building (i.e., who's calling main). Are you coding an app that uses the C-runtime? A Maya plugin? A Windows service? A driver? Each case will require research to see if exit is equivalent to return. IMHO using exit when you really mean return just makes the code more confusing. OTOH, if you really do mean exit, then by all means use it.

jwfearn
  • 26,394
  • 26
  • 89
  • 117
  • ISO C guarantees that whatever happens when main returns is equivalent to main's caller passing the return value to `exit()`. [Return vs Exit from main function in C](https://stackoverflow.com/q/63309629) has some answers that quote the standard. If your program isn't running as a stand-alone process, the first function probably isn't called `main`. If it is, you're doing some weird voodoo and not in ISO C land anymore. – Peter Cordes Aug 08 '20 at 23:05
17

There is at least one reason to prefer exit: If any of your atexit handlers refer to automatic-storage-duration data in main, or if you used setvbuf or setbuf to assign to one of the standard streams an automatic-storage-duration buffer in main, then returning from main produces undefined behavior, but calling exit is valid.

Another potential usage (usually reserved for toy programs, however) is to exit from a program with recursive invocations of main.

R.. GitHub STOP HELPING ICE
  • 195,354
  • 31
  • 331
  • 669
  • This is interesting, because [in C, at least] [5.1.2.2.3p1](http://www.iso-9899.info/n1570.html#5.1.2.2.3p1) states that `return 0;` from the initial call to `main` is equivalent to `exit(0);`... Is it possible that this might be a *compiler bug* (or a *compiler feature*, depending upon who you ask)? – autistic Dec 09 '15 at 14:16
  • 1
    @Seb there is nothing special about `main()` -- it's just a function like any other. On the other hand since it has special mention in the standard, the standard must be fairly careful about how it defines `main()` and things near and dear to it. However in the end though the standard does not (and *must not*) require compilers to do anything special about auto storage in `main()`. Please take care to read [Footnote #11](http://www.iso-9899.info/n1570.html#FOOTNOTE.11) below the paragraph you referenced in your comment. – Greg A. Woods Dec 09 '15 at 19:21
  • 1
    @GregA.Woods Interesting. It seems that there's some normative text in contradiction to some informative text. According to [the ISO/IEC directives](http://www.iec.ch/members_experts/refdocs/iec/isoiec-dir2%7Bed6.0%7Den.pdf), the normative reference is considered "indispensable", where-as the informative is considered supplementary only... Additionally, the use of the word "will" to convey a requirement is invalid; according to the aforementioned document (Annex H). In summary, the informative text is most certainly invalid. – autistic Dec 10 '15 at 06:58
  • 2
    @Seb: The intent is obviously not to override the requirements on the behavior of automatic storage and the footnote was obviously written to clarify this. Yes there is imprecise, poor wording in the C standard. Anyone who has read it knows this. We also know that the committee generally does not fix issues like this because the intent is already obvious. – R.. GitHub STOP HELPING ICE Dec 10 '15 at 16:25
  • @R.. Regardless, the normative text is indispensable, while the informative text can be stripped and the stripped standard will still be valid. – autistic Dec 11 '15 at 02:42
  • 1
    @Seb: This is not a debate or court case to prove you're right. The goal *should* be obtaining a clear understanding of what the actual C language (as intended and as implemented) is, and expressing that in answers that are useful to readers. The normative text is subtly wrong (contrary to the intent of what it was supposed to express) in a way that's essentially fixed by the footnote. If you're not happy with this, submit a defect report, but don't expect a reply. That's how WG14 rolls... – R.. GitHub STOP HELPING ICE Dec 11 '15 at 04:18
  • @R.. I agree; there is nothing to debate, though you say one thing and then you continue to debate later on.. The clear understanding of what the actual C language is can be obtained by compiling all of the (indispensable) normative text together, and discarding the informative text. Try compiling the informative text together, however, and you will not form a clear understanding of the C language. If you wanted to continue this debate, you should probably refer to the section of the normative text that the informative text refers to. In that case, there are many sections that contradict this, – autistic Dec 11 '15 at 06:13
  • @R.. ... in fact, I have no doubt that some section of the standard states that a function declared to return a non-`void` type that lacks a `return` statement has X behaviour, where X is implementation-defined or undefined... but the fact is that 5.1.2.2 documents an alternate behaviour for a specific instance (being the `main` entrance point). – autistic Dec 11 '15 at 06:15
  • 3
    @Seb: You seem to believe the C language can be understood by interpreting the natural-language text of the standard as if it's completely rigorous. This is simply not possible. The specification contains mistakes, and WG14 does not waste their time rewriting stuff when a simple footnote clarifies that they already know they made a mistake but that the reader can make sense of it. – R.. GitHub STOP HELPING ICE Dec 11 '15 at 17:53
  • Just informationally, with gcc functions registered with `atexit(3)` run whether you call `exit(3)` or issue a return. – Kevin Lyda Jan 25 '18 at 15:31
5

Does exit() do anything special that 'return' doesn't?

With some compilers for uncommon platforms, exit() might translate its argument into your program's exit value while a return from main() might just pass the value directly to the host environment without any translation.

The standard requires identical behavior in these cases (specifically, it says returning something that's int-compatible from main() should be equivalent to calling exit() with that value). The problem is that different OSes have different conventions for interpreting the exit values. On many (MANY!) systems, 0 means success and anything else is a failure. But on, say, VMS, odd values mean success and even ones mean failure. If you returned 0 from main(), a VMS user would see a nasty message about an access violation. There wasn't actually an access violation--that was simply the standard message associated with failure code 0.

Then ANSI came along and blessed EXIT_SUCCESS and EXIT_FAILURE as arguments you could pass to exit(). The standard also says that exit(0) should behave identically to exit(EXIT_SUCCESS), so most implementations define EXIT_SUCCESS to 0.

The standard, therefore, puts you in a bind on VMS, as it leaves no standard way to return a failure code that happens to have the value 0.

The early-1990s era VAX/VMS C compiler therefore did not interpret the return value from main(), it simply returned whatever value to the host environment. But if you used exit() it would do what the standard required: translate EXIT_SUCCESS (or 0) into a success code and EXIT_FAILURE into a generic failure code. To use EXIT_SUCCESS, you had to pass it to exit(), you could not return it from main(). I don't know whether more modern versions of that compiler preserved that behavior.

A portable C program used to look like this:

#include <stdio.h>
#include <stdlib.h>

int main() {
  printf("Hello, World!\n");
  exit(EXIT_SUCCESS);  /* to get good return value to OS */
  /*NOTREACHED*/ /* to silence lint warning */
  return 0;  /* to silence compiler warning */
}

Aside: If I recall correctly, the VMS convention for exit values is more nuanced than odd/even. It actually uses something like the low three bits to encode a severity level. Generally speaking, however, the odd severity levels indicated success or miscellaneous information and the even ones indicated errors.

Adrian McCarthy
  • 41,073
  • 12
  • 108
  • 157
  • Some old pre-ANSI compilers might have treated the value `returned` by `main` differently from the value passed to `exit` -- but the standard specifically says, "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". That's C11; C89/C90 had nearly the same wording. – Keith Thompson Aug 26 '15 at 23:46
  • Indeed. Nevertheless, some ANSI-era compilers didn't get this right and required explicit use of exit to get the correct return value returned to the host environment. Since the standard (even then) requires 0 to be treated the same as `EXIT_SUCCESS`, there was no way to return a platform-specific _failure_ status with the value 0, which may be why some of the compilers of the era treated return-from-main and `exit()` differently. – Adrian McCarthy Aug 27 '15 at 16:34
  • Do you have a citation for that? A separate issue is whether any *current* compilers have that particular bug. Your answer is for phrased in the present tense. – Keith Thompson Aug 27 '15 at 16:38
  • That's a fair criticism. I've changed the wording to limit the scope to the specific case I know about. – Adrian McCarthy Aug 28 '15 at 21:57
5

I always use return because the standard prototype for main() says that it does return an int.

That said, some versions of the standards give main special treatment and assume that it returns 0 if there's no explicit return statement. Given the following code:

int foo() {}
int main(int argc, char *argv[]) {}

G++ only generates a warning for foo() and ignores the missing return from main:

% g++ -Wall -c foo.cc
foo.cc: In function ‘int foo()’:
foo.cc:1: warning: control reaches end of non-void function
Alnitak
  • 313,276
  • 69
  • 379
  • 466
  • I don't know about C, but the C++ standard specifies that if you don't return a value in main, it's assumed to return 0. – Jason Baker Jan 20 '09 at 14:49
  • It seems as though C99 is the same: http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?id=1043284376&answer=1044841143 – Jason Baker Jan 20 '09 at 14:51
  • 3
    C99 and C++ return 0 if there is no return statement, C90 doesn't. – d0k Jan 20 '09 at 14:57
  • Just because a function is declared as having a return value doesn't mean you must use `return` to end its execution. Calling `exit()` is also a valid, and sometimes necessary, way to end the execution of any function. Indeed as I and others have described elsewhere, calling `exit()` even from `main()` conveys a far more clear intention of exiting the whole process, preserves automatic storage until process exit, and makes for easier maintenance during future refactoring of code. For C using `return` in `main()` when the intent is to end the process is thus arguably a bad practice. – Greg A. Woods Mar 15 '16 at 09:11
  • @GregA.Woods that's your opinion, but hardly merits a down-vote! What I've written above is _completely consistent with the standard_, whereas your argument is just semantics. – Alnitak Mar 15 '16 at 09:13
  • On the contrary, your answer as written is lacking advice about good programming practices. Advising strict minimal adherence to standards and definitions, especially when there is a clear corner case where undefined behaviour can be triggered, and in addition where maintainability is adversely affected, is not _good_ advice. – Greg A. Woods Mar 15 '16 at 09:17
  • 1
    I've _never_ had a situation where it was "necessary" to call `exit()` instead of using `return` in main. On the other hand, I *have* had problems when wrapping a call to `main()` that used `exit()` unnecessarily. The vast majority of the answers and commentary here appear to disagree with your assertion that using `return` in `main()` is "a bad practise". – Alnitak Mar 15 '16 at 09:25
  • *".... and assume that it returns 0 if there's no explicit return statement"* .. and what is wrong with that ? do you mean that If I used `exit()` while there is some error that I should return non-zero for, the program will return 0 instead ? is that what you mean ? – Accountant م Apr 02 '19 at 21:24
5

I STRONGLY second the comment by R. about using exit() in order to avoid having automatic storage in main() reclaimed before the program actually ends. A return X; statement in main() is not precisely equivalent to a call to exit(X);, since the dynamic storage of main() vanishes when main() returns, but it it does not vanish if a call to exit() is made instead.

Furthermore, in C or any C-like language a return statement strongly hints to the reader that execution will continue in the calling function, and while this continuation of execution is usually technically true if you count the C startup routine which called your main() function, it's not exactly what you mean when you mean to end the process.

After all, if you want to end your program from within any other function except main() you must call exit(). Doing so consistently in main() as well makes your code much more readable, and it also makes it much easier for anyone to re-factor your code; i.e. code copied from main() to some other function won't misbehave because of accidental return statements that should have been exit() calls.

So, combining all of these points together the conclusion is that it's a bad habit, at least for C, to use a return statement to end the program in main().

Greg A. Woods
  • 2,251
  • 24
  • 23
  • You might find [5.1.2.2.3p1 of the C standard](http://www.iso-9899.info/n1570.html#5.1.2.2.3p1) interesting... – autistic Dec 09 '15 at 14:24
  • 1
    This answer is worth careful consideration for C programs, as contextually indicated in the answer. For use with C++, it needs to be carefully weighed with against all the previously mentioned caveats. For C++, I would suggest to avoid `exit()` in general, but do use it if a `throw` or `abort()` alternatives do not work in a specific context. But especially avoid `exit()` in main, and to use return in main instead as typical practice. – Eljay Jan 04 '18 at 16:40
0

In C returning from main is exactly the same as calling exit with the same value.

Section 5.1.2.2.3 of the C standard states:

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

The rules for C++ are a bit different as mentioned in other answers.

dbush
  • 162,826
  • 18
  • 167
  • 209
-1

There actually IS a difference between exit(0) and return(0) in main – when your main function is called multiple times.

The following program

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

Run as

./program 0 0 0 0

Will result in following output:

00000

However this one:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

Won't print anything regardless of the arguments.

If you are sure that nobody will ever call your main explicitly it is not technically a big difference in general, but to maintain clearer code exit would look much better. If you for some reason want to call main – you should adjust it to your needs.

Speaking about C.

radrow
  • 4,248
  • 1
  • 19
  • 36