Prev | Contents | Next

28 Exiting a Program

Turns out there are a lot of ways to do this, and even ways to set up “hooks” so that a function runs when a program exits.

In this chapter we’ll dive in and check them out.

We already covered the meaning of the exit status code in the Exit Status section, so jump back there and review if you have to.

All the functions in this section are in <stdlib.h>.

28.1 Normal Exits

We’ll start with the regular ways to exit a program, and then jump to some of the rarer, more esoteric ones.

When you exit a program normally, all open I/O streams are flushed and temporary files removed. Basically it’s a nice exit where everything gets cleaned up and handled. It’s what you want to do almost all the time unless you have reasons to do otherwise.

28.1.1 Returning From main()

If you’ve noticed, main() has a return type of int… and yet I’ve rarely, if ever, been returning anything from main() at all.

This is because for main() only (and I can’t stress enough this special case only applies to main() and no other functions anywhere) has an implicit return 0 if you fall off the end.

You can explicitly return from main() any time you want, and some programmers feel it’s more Right to always have a return at the end of main(). But if you leave it off, C will put one there for you.

So… here are the return rules for main():

28.1.2 exit()

This one has also made an appearance a few times. If you call exit() from anywhere in your program, it will exit at that point.

The argument you pass to exit() is the exit status.

28.1.3 Setting Up Exit Handlers with atexit()

You can register functions to be called when a program exits whether by returning from main() or calling the exit() function.

A call to atexit() with the handler function name will get it done. You can register multiple exit handlers, and they’ll be called in the reverse order of registration.

Here’s an example:

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

void on_exit_1(void)
{
    printf("Exit handler 1 called!\n");
}

void on_exit_2(void)
{
    printf("Exit handler 2 called!\n");
}

int main(void)
{
    atexit(on_exit_1);
    atexit(on_exit_2);
    
    printf("About to exit...\n");
}

And the output is:

About to exit...
Exit handler 2 called!
Exit handler 1 called!

28.2 Quicker Exits with quick_exit()

This is similar to a normal exit, except:

But there is a way to register exit handlers: call at_quick_exit() analogously to how you’d call atexit().

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

void on_quick_exit_1(void)
{
    printf("Quick exit handler 1 called!\n");
}

void on_quick_exit_2(void)
{
    printf("Quick exit handler 2 called!\n");
}

void on_exit(void)
{
    printf("Normal exit--I won't be called!\n");
}

int main(void)
{
    at_quick_exit(on_quick_exit_1);
    at_quick_exit(on_quick_exit_2);

    atexit(on_exit);  // This won't be called

    printf("About to quick exit...\n");

    quick_exit(0);
}

Which gives this output:

About to quick exit...
Quick exit handler 2 called!
Quick exit handler 1 called!

It works just like exit()/atexit(), except for the fact that file flushing and cleanup might not be done.

28.3 Nuke it from Orbit: _Exit()

Calling _Exit() exits immediately, period. No on-exit callback functions are executed. Files won’t be flushed. Temp files won’t be removed.

Use this if you have to exit right fargin’ now.

28.4 Exiting Sometimes: assert()

The assert() statement is used to insist that something be true, or else the program will exit.

Devs often use an assert to catch Should-Never-Happen type errors.

#define PI 3.14159

assert(PI > 3);   // Sure enough, it is, so carry on

versus:

goats -= 100;

assert(goats >= 0);  // Can't have negative goats

In that case, if I try to run it and goats falls under 0, this happens:

goat_counter: goat_counter.c:8: main: Assertion `goats >= 0' failed.
Aborted

and I’m dropped back to the command line.

This isn’t very user-friendly, so it’s only used for things the user will never see. And often people write their own assert macros that can more easily be turned off.

28.5 Abnormal Exit: abort()

You can use this if something has gone horribly wrong and you want to indicate as much to the outside environment. This also won’t necessarily clean up any open files, etc.

I’ve rarely seen this used.

Some foreshadowing about signals: this actually works by raising a SIGABRT which will end the process.

What happens after that is up to the system, but on Unix-likes, it was common to dump core170 as the program terminated.


Prev | Contents | Next