83

There is question about using exit in C++. The answer discusses that it is not good idea mainly because of RAII, e.g., if exit is called somewhere in code, destructors of objects will not be called, hence, if for example a destructor was meant to write data to file, this will not happen, because the destructor was not called.

I was interested how is this situation in C. Are similar issues applicable also in C? I thought since in C we don't use constructors/destructors, situation might be different in C. So is it ok to use exit in C? For example I have seen following functions sometimes used in C:

void die(const char *message)
{
    if(errno) {
        perror(message);
    } else {
        printf("ERROR: %s\n", message);
    }

    exit(1);
}
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
  • 2
    "destructors of objects will not be called" -- That is not entirely correct (see: http://www.cplusplus.com/reference/cstdlib/exit/). You are thinking of quick_exit (see: http://www.cplusplus.com/reference/cstdlib/quick_exit/?kw=quick_exit). – Coder Jul 19 '15 at 12:34
  • You might also have some operating system specific issues, e.g. the *conventional* role of `SIGTERM` signal on POSIX & Linux.... Well behaved servers are expected to handle it nicely. And you should avoid using `SIGKILL` (i.e. try `kill -TERM` then `kill -QUIT` and only later then `kill -KILL` as a sysadmin) – Basile Starynkevitch Jul 19 '15 at 12:49
  • 21
    How is this not a duplicate nearly 7 years after Stack Overflow launched? – Peter Mortensen Jul 19 '15 at 17:31
  • 7
    @PeterMortensen: it _is_ surprising, but I'm not aware of a good alternative which this duplicates. The high-voted [Use of `exit()` function](http://stackoverflow.com/questions/2425167/) is not germane (and the high vote count is surprising — it isn't a good question). [Why should I not use exit() function in C?](http://stackoverflow.com/questions/27069451) would be a good candidate if it had answers of the calibre of this question. It doesn't; a reverse close of that as a duplicate of this is appropriate — and I've done it. – Jonathan Leffler Jul 19 '15 at 19:11
  • I'm not sure you want to use 'printf' in error case. The function uses buffers, but if you had a memory corruption, those buffers may not be good. You might want to just use 'write(2, "ERROR: ", 7); write(2, message, strlen(message)); – Alex Jul 22 '15 at 18:23
  • @Alex: the error messages should normally be written to standard error (`stderr`), which is normally line buffered (is not fully buffered) so that messages will appear in a timely manner. This is more flexible than using `write(2, …)` or `write(STDERR_FILENO, …)`. Modern POSIX does define [`dprintf()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html), but it doesn't specify how it handles writing to the file descriptor. – Jonathan Leffler Jul 25 '19 at 20:17
  • Alternatively, or additionally, the messages might be written to log files via some logging system. The Java package `log4j` is well-known; there is an analogous [`log4c`](http://log4c.sourceforge.net/) library on SourceForge, but it has not been updated for a while (the page linked has an end copyright date of 2004, and the compiler and operating system versions are more or less consistent with that era). There's at least one fork of it on GitHub. I've not used it myself. – Jonathan Leffler Jul 25 '19 at 20:23
  • It looks like there was a release [1.2.4 of log4c](https://sourceforge.net/projects/log4c/files/log4c/) made on 2013-10-03. And some source code changes were made in [Git](https://sourceforge.net/p/log4c/log4c/ci/master/tree/) as recently as 2016-01-30. I've still not actually tried it. – Jonathan Leffler Jun 30 '22 at 23:59

7 Answers7

84

Rather than abort(), the exit() function in C is considered to be a "graceful" exit.

From C11 (N1570) 7.22.4.4/p2 The exit function (emphasis mine):

The exit function causes normal program termination to occur.

The Standard also says in 7.22.4.4/p4 that:

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.

It is also worth looking at 7.21.3/p5 Files:

If the main function returns to its original caller, or if the exit function is called, all open files are closed (hence all output streams are flushed) before program termination. Other paths to program termination, such as calling the abort function, need not close all files properly.

However, as mentioned in comments below you can't assume that it will cover every other resource, so you may need to resort to atexit() and define callbacks for their release individually. In fact it is exactly what atexit() is intended to do, as it says in 7.22.4.2/p2 The atexit function:

The atexit function registers the function pointed to by func, to be called without arguments at normal program termination.

Notably, the C standard does not say precisely what should happen to objects of allocated storage duration (i.e. malloc()), thus requiring you be aware of how it is done on particular implementation. For modern, host-oriented OS it is likely that the system will take care of it, but still you might want to handle this by yourself in order to silence memory debuggers such as Valgrind.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • 3
    This seems to me the correct answer. A socket connection is closed on exit because the process ending closes all file descriptors whether or not opened with `stdio`, i.e. is equivalent to a `close()` on the FD. I don't know in what sense @BlueMoon thinks that's incorrect, but it is no less incorrect than a call to `close()`, and if further clear-up is required, that's precisely what `atexit()` is for. – abligh Jul 19 '15 at 12:52
  • 1
    @abligh Modern OSs will close all the fds associated with that process; but that's something brutal and not standard (what Blue Moon is saying). – edmz Jul 19 '15 at 12:58
  • 3
    @black if further clearup is needed, `atexit()` can be used. Using `exit()` itself is no more brutal than doing `return` from `main()`. – abligh Jul 19 '15 at 13:09
  • 5
    Writing 'proper' software using 'good practices' is why I have so many apps that don't shut down immediately when asked to.:( If the OS can do something, you should let it do it instead of faffing around with user code trying do something that already exists, is already tested and already debugged. If your software system cannot withstand a sudden, unexpected shutdown, (eg. from some thread calling for in immediate in-process termination, Task Manager 'End process', 'kill -9' or power failure), it is of poor quality anyway. – Martin James Jul 19 '15 at 13:41
  • 1
    it will be nice if you can add -if possible- to your answer what kind of resources might one need to release in `atexit`, otherwise it is ok. @BlueMoon: I don't think using function such as `die` means one can't program, in several occasions one might want to use it. Otherwise indeed, you can also handle this using just return values, etc. – Giorgi Moniava Jul 19 '15 at 17:45
  • 1
    @Giorgi: I'd love to list them all, but they are vast and application and API specific. For instance, if you write a RDBMS client, then it you might need to release database connection.If you are working with low-level USB transactions, then you might need to somehow cancel connection with device, so it does not hang. For GPU programming you might need to release device context. For GDI, .., etc. – Grzegorz Szpetkowski Jul 19 '15 at 18:10
  • @GrzegorzSzpetkowski:hmm,ok I see. For those ones, `exit` might not free them .. But for sockets and files should be ok?(as I read in comments I think state of sockets is also not clear..interesting are cases when not closing socket means data would not be sent which was waiting to be sent for example) – Giorgi Moniava Jul 19 '15 at 18:14
  • @Giorgi: Yes, it might not (to that point, that C Standard does not guarantee that explicitely. as it is too broad). I am not sure about web sockets. but if they are based on file descriptors on particular implementation and there is nothing more, then these are fine to be handled by `exit`. – Grzegorz Szpetkowski Jul 19 '15 at 18:18
  • @GrzegorzSzpetkowski: I see – Giorgi Moniava Jul 19 '15 at 18:19
23

Yes, it is ok to use exit in C.

To ensure all buffers and graceful orderly shutdown, it would be recommended to use this function atexit, more information on this here

An example code would be like this:

void cleanup(void){
   /* example of closing file pointer and free up memory */
   if (fp) fclose(fp);
   if (ptr) free(ptr);
}

int main(int argc, char **argv){
   /* ... */
   atexit(cleanup);
   /* ... */
   return 0;
}

Now, whenever exit is called, the function cleanup will get executed, which can house graceful shutdown, clean up of buffers, memory etc.

t0mm13b
  • 34,087
  • 8
  • 78
  • 110
  • @IlmariKaronen Allocated memory is not handled automatically in any way. It is the OS which *might* make sure the memory gets reclaimed back to the OS. Grzegorz Szpetkowski's argument is opposing your statement: *Notably, the C standard does not say precisely what should happen to objects of allocated storage duration (i.e.malloc()), thus requiring you be aware of how it is done on particular implementation. For modern, host-oriented OS it is likely that the system will take care of it, but still you might want to handle this by yourself in order to silence memory debuggers such as Valgrind.* – this Jul 20 '15 at 01:20
15

You don't have constructors and destructors but you could have resources (e.g. files, streams, sockets) and it is important to close them correctly. A buffer could not be written synchronously, so exiting from the program without correctly closing the resource first, could lead to corruption.

enrico.bacis
  • 30,497
  • 10
  • 86
  • 115
  • 8
    This answer is a great example of "don't learn a programming language; learn how to program". You can't generally avoid understanding the problem by choosing a different language. – Kerrek SB Jul 19 '15 at 12:34
  • 11
    I believe this to be incorrect. If you call `exit()` (rather than `_exit()`) the `atexit` routines are called and `stdio` buffering is flushed to disk. `exit()` is precisely there to allow an orderly exit of the program. – abligh Jul 19 '15 at 12:50
  • @abligh I believe you may still incur in something not checked from some other libraries, it could be database connection or anything. I prefer to have a function that closes any resource before exiting the program. I understand the file example is not the best fit but it is easily comprehensible as problem generated by unclosed resources. – enrico.bacis Jul 19 '15 at 13:01
  • 1
    @enrico.bacis: what resources can be left in use after a process ends? What graceful shutdown could not be handled by `atexit()`? Using `exit()` is no different from the other way of quitting (a `return` from `main()`). – abligh Jul 19 '15 at 13:08
  • Sure, but I personally close all the resources even before returning from main. Because I do think that is the correct way of thinking about programming, even if c is smart and can do something for me. – enrico.bacis Jul 19 '15 at 13:11
  • 2
    @abligh Real time systems don't free malloc'ed memory on exit(), for example, resulting in leaks. POSIX shared memory is another example. So it depends on the environment rather than strictly following the C standard. – P.P Jul 19 '15 at 13:32
  • 2
    @abligh: Not all resources are standard library provided. While you're certainly right that the standard library makes certain guarantees, you still need to think about the global control flow of your program and the *responsibilities* of each of its parts, and ensure that each pending responsibility is dealt with appropriately. The term "resource" is a concise term for this general notion and transcends any particular library, and handling resources is ultimately the responsibility of the programmer. Facilities like `atexit` can help, though they are not always appropriate. – Kerrek SB Jul 19 '15 at 18:04
  • @KerrekSB I am not arguing you shouldn't clear up resources. But having done exactly what you recommend, and cleared up your resources, how exactly would you recommend exiting the program, other than using `exit()`; as far as I know the only vaguely sensible way would be a `return` from `main()`. `exit()` is (IMHO) better than that. – abligh Jul 19 '15 at 19:49
  • 2
    @abligh: You *can* of course use `exit` if you want to, but it is a global jump in control that comes with all the downsides of unstructured control that we've discussed. As a way to terminate from a failure condition it may be appropriate (and it's seems to be what the OP wants), though for normal control flow I'd probably prefer designing the main loop with a proper exit condition so that you indeed end up returning from main. – Kerrek SB Jul 19 '15 at 19:57
10

Using exit() is OK

Two major aspects of code design that have not yet been mentioned are 'threading' and 'libraries'.

In a single-threaded program, in the code you're writing to implement that program, using exit() is fine. My programs use it routinely when something has gone wrong and the code isn't going to recover.

But…

However, calling exit() is a unilateral action that can't be undone. That's why both 'threading' and 'libraries' require careful thought.

Threaded programs

If a program is multi-threaded, then using exit() is a dramatic action which terminates all the threads. It will probably be inappropriate to exit the entire program. It may be appropriate to exit the thread, reporting an error. If you're cognizant of the design of the program, then maybe that unilateral exit is permissible, but in general, it will not be acceptable.

Library code

And that 'cognizant of the design of the program' clause applies to code in libraries, too. It is very seldom correct for a general purpose library function to call exit(). You'd be justifiably upset if one of the standard C library functions failed to return just because of an error. (Obviously, functions like exit(), _Exit(), quick_exit(), abort() are intended not to return; that's different.) The functions in the C library therefore either "can't fail" or return an error indication somehow. If you're writing code to go into a general purpose library, you need to consider the error handling strategy for your code carefully. It should fit in with the error handling strategies of the programs with which it is intended to be used, or the error handling may be made configurable.

I have a series of library functions (in a package with header "stderr.h", a name which treads on thin ice) that are intended to exit as they're used for error reporting. Those functions exit by design. There are a related series of functions in the same package that report errors and do not exit. The exiting functions are implemented in terms of the non-exiting functions, of course, but that's an internal implementation detail.

I have many other library functions, and a good many of them rely on the "stderr.h" code for error reporting. That's a design decision I made and is one that I'm OK with. But when the errors are reported with the functions that exit, it limits the general usefulness the library code. If the code calls the error reporting functions that do not exit, then the main code paths in the function have to deal with error returns sanely — detect them and relay an error indication to the calling code.


The code for my error reporting package is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • That's well pointed. You may see [here](https://gmplib.org/repo/gmp-6.0/file/2ff56d3c5dfe/memory.c) an example of library that calls `abort()` when it fais to allocate memory by either `malloc()` or `realloc()`, Imagine, that you have an application, that links with 100 libraries and you wonder which one and how crashed your application. What's more I haven't found any mention of `abort()` in their documention (but don't get me wrong. it's great library for its purpose). – Grzegorz Szpetkowski Jul 19 '15 at 18:42
  • @GrzegorzSzpetkowski And it doesn't even manually call flush after fprinting to stderr. Ouch! – this Jul 20 '15 at 01:26
  • 1
    @this: It shouldn't need to do so. The output from `stderr` is normally line buffered. If the output terminates with a newline, it will be flushed by the system anyway. – Jonathan Leffler Jul 20 '15 at 02:03
  • @JonathanLeffler Relying on the user, not a smart move. – this Jul 20 '15 at 16:01
  • @this: No, relying on the implementation adhering to the requirements of the C standard: §7.21.3 ¶7 _At program startup, three text streams are predefined and need not be opened explicitly — standard input (for reading conventional input), standard output (for writing conventional output), and standard error (for writing diagnostic output). As initially opened, the standard error stream is not fully buffered; …_ It requires active coding by the programmer to make standard error fully buffered (and there's no help for anyone if the programmer is that obtuse — except "do not use the code"). – Jonathan Leffler Jul 20 '15 at 16:09
  • @JonathanLeffler Why would using setvbut be a reason for calling someone obtuse? Ad-hominem is not an argument. I would rather not be using code that makes assumptions, which can be simply avoided. This reduces the amount of potential mistakes and/or annoyances. – this Jul 20 '15 at 16:10
  • @this: I was being polite. (This site can't afford to be NSFW.) Someone who sets standard error to fully buffered is subverting the reasonable expectations of programmers. Such people should not be allowed to release code for other people to use. – Jonathan Leffler Jul 20 '15 at 16:45
  • That depends on your definition of reasonable expectation, which is subjective. You haven't presented an argument in your support. On the other hand, I have demonstrated that by calling flush() manually you always reduce, and cannot increase, the amount of potential errors, that can be caused by calling functions made available by the C library. Thus omitting the flush is always less safe and introduces potential errors. – this Jul 20 '15 at 17:04
  • @this: deliberately subverting the documented behavior of the standard is something that could be done for a specific project but it is reasonable for code to assume that stNdard error is not fully buffered. If you choose to make it buffered then you will get errors missing that line buffered mode would prevent. End of discussion; further comment will not be constructive. – Jonathan Leffler Jul 20 '15 at 17:14
  • Fully buffered is well defined and documented by the Standard. In fact, Standard defines states where the buffer *begins* as fully buffered. A code that make assumptions in spite of that documentation is always worse than the code that does. ( See my previous comment ). I accept your withdrawal from the discussion as conceding to my argument. – this Jul 20 '15 at 17:21
  • @this: no; I do not concede that your argument has validity. I just recognize that we are not going to agree and so I am willing to agree that we disagree. I should have made that clear in my previous comment. – Jonathan Leffler Jul 20 '15 at 18:01
6

One reason to avoid exit in functions other than main() is the possibility that your code might be taken out of context. Remember, exit is a type of non local control flow. Like uncatchable exceptions.

For example, you might write some storage management functions that exit on a critical disk error. Then someone decides to move them into a library. Exiting from a library is something that will cause the calling program to exit in an inconsitent state which it may not be prepared for.

Or you might run it on an embedded system. There is nowhere to exit to, the whole thing runs in a while(1) loop in main(). It might not even be defined in the standard library.

pjc50
  • 1,856
  • 16
  • 18
  • 3
    And people shouldn't be allowed to own kitchen knives, because somebody might grab a handful of them and try to juggle them, or play "catch" with his kid.  Obviously, I disagree.  If somebody decides to copy my code into his program, and my code turns out not to suit his purposes, that's his problem, for failing to read the code he's assimilating. – G-Man Says 'Reinstate Monica' Jul 19 '15 at 17:52
  • 4
    A rather crass analogy. C is full of things that *can* be done but should probably be avoided. Hence the existence of the IOCCC. Note that my post does not say "should not". – pjc50 Jul 19 '15 at 21:19
2

Depending on what you are doing, exit may be the most logical way out of a program in C. I know it's very useful for checking to make sure chains of callbacks work correctly. Take this example callback I used recently:

unsigned char cbShowDataThenExit( unsigned char *data, unsigned short dataSz,unsigned char status)
{

    printf("cbShowDataThenExit with status %X (dataSz %d)\n", status, dataSz);
    printf("status:%d\n",status);
    printArray(data,dataSz);
    cleanUp();
    exit(0);
}

In the main loop, I set everything up for this system and then wait in a while(1) loop. It is possible to make a global flag to exit the while loop instead, but this is simple and does what it needs to do. If you are dealing with any open buffers like files and devices you should clean them up before close for consistency.

Dom
  • 1,687
  • 6
  • 27
  • 37
-1

It is terrible in a big project when any code can exit except for coredump. Trace is very import to maintain a online server.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278