exit() / atexit() / abort()
| Since: | C89(1989) |
|---|
Functions for terminating a program. There are three types — normal termination, error termination, and forced termination — and you choose the right one depending on the situation.
Syntax
// Terminates the program normally. Runs atexit-registered functions, flushes buffers, and removes temporary files. // status: pass EXIT_SUCCESS(0) or EXIT_FAILURE(1). void exit(int status); // Registers a function to be called when exit() is invoked (at least 32 functions can be registered). // Registered functions are called in reverse order (LIFO). // Return value: 0 on success, non-zero on failure. int atexit(void (*func)(void)); // Terminates the program abnormally. Does not run atexit functions or flush buffers. // Raises the SIGABRT signal and generates a core dump. void abort(void); // Terminates the program immediately. Unlike exit(), performs no cleanup at all. void _Exit(int status); // C99 and later
Comparison of Termination Functions
| Function | Runs atexit | Flushes Buffers | Use Case |
|---|---|---|---|
| exit() | Yes | Yes | Standard normal or error termination. |
| _Exit() | No | No | Immediate exit with no cleanup. Useful for terminating a forked child process. |
| abort() | No | No | Abnormal termination on unrecoverable errors. Also called internally by assert(). |
| return (main) | Yes | Yes | Returning from main() is equivalent to calling exit(). This is the preferred approach. |
Sample Code
sample_exit_atexit_abort.c
#include <stdio.h>
#include <stdlib.h>
// Cleanup functions registered with atexit.
void cleanup_a(void) { printf("cleanup_a: Removed temporary files.\n"); }
void cleanup_b(void) { printf("cleanup_b: Closed the log.\n"); }
int main(void) {
// Register cleanup functions with atexit (called in reverse order).
atexit(cleanup_a);
atexit(cleanup_b); // Called in order: cleanup_b → cleanup_a.
printf("Starting process.\n");
// Exit on unrecoverable errors such as a failed file open.
FILE *fp = fopen("config.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Configuration file not found.\n");
exit(EXIT_FAILURE); // Runs atexit-registered functions before exiting.
}
// In this sample, fopen fails and exit() is called.
fclose(fp);
return EXIT_SUCCESS;
}
Common Mistakes
Common Mistake: Assuming atexit Handlers Run After abort
Calling abort() does not invoke functions registered with atexit(). If important cleanup is needed, use exit() or return instead.
exit_vs_abort.c
#include <stdio.h>
#include <stdlib.h>
void save_data(void) { printf("save_data: data saved.\n"); }
int main(void) {
atexit(save_data);
int error_type = 1;
if (error_type == 1) {
/* exit() → save_data is called */
fprintf(stderr, "Recoverable error. Exiting with exit().\n");
exit(EXIT_FAILURE);
} else {
/* abort() → save_data is NOT called */
fprintf(stderr, "Unrecoverable error. Aborting.\n");
abort();
}
}
Run the following command:
gcc exit_vs_abort.c -o exit_vs_abort ./exit_vs_abort Recoverable error. Exiting with exit(). save_data: data saved.
Notes
Returning from main() and calling exit(status) are equivalent. Use exit() only when you need to terminate immediately from a deeply nested function; in most cases, returning normally makes the code flow easier to follow.
Functions registered with atexit() are only called when exit() is used. They are not called on abort() or signal-based forced termination. Open files are automatically flushed and closed on exit(), but dynamically allocated memory is not freed — the OS reclaims it when the process ends.
For checking program state, see also assert().
If you find any errors or copyright issues, please contact us.