Data Structures and Algorithms
2.7 Error Handling

No program or program fragment can be considered complete until appropriate error handling has been added. Unexpected program failures are a disaster - at the best, they cause frustration because the program user must repeat minutes or hours of work, but in life-critical applications, even the most trivial program error, if not processed correctly, has the potential to kill someone.

If an error is fatal, in the sense that a program cannot sensibly continue, then the program must be able to "die gracefully". This means that it must

2.7.1 Defining Errors

The first step in determining how to handle errors is to define precisely what is considered to be an error. Careful specification of each software component is part of this process. The pre-conditions of an ADT's methods will specify the states of a system (the input states) which a method is able to process. The post-conditions of each method should clearly specify the result of processing each acceptable input state. Thus, if we have a method:
int f( some_class a, int i )
/* PRE-CONDITION: i >= 0 */
/* POST-CONDITION:
    if ( i == 0 )
        return 0 and a is unaltered
    else
        return 1 and update a's i-th element by .... */ 
Thus, a complete specification will specify By specifying the acceptable input states in pre-conditions, it will also divide responsibility for errors unambiguously.

2.7.2 Processing errors

Let's look at an error which must be handled by the constructor for any dynamically allocated object: the system may not be able to allocate enough memory for the object.

A good way to create a disaster is to do this:

X ConsX( .... )
    {
    X x = malloc( sizeof(struct t_X) );
    if ( x == NULL ) {
        printf("Insuff mem\n"); exit( 1 );
        }
    else
       .....
    }
Not only is the error message so cryptic that it is likely to be little help in locating the cause of the error (the message should at least be "Insuff mem for X"!), but the program will simply exit, possibly leaving the system in some unstable, partially updated, state. This approach has other potential problems:
As a general rule, I/O is non-portable!
A function like printf will produce error messages on the 'terminal' window of your modern workstation, but if you are running a GUI program like Netscape, where will the messages go?

So, the same function may not produce useful diagnostic output for two programs running in different environments on the same processor! How can we expect it to be useful if we transport this program to another system altogether, eg a Macintosh or a Windows machine?

Before looking at what we can do in ANSI C, let's look at how some other languages tackle this problem.

Continue on to Ada Exceptions
Back to the Table of Contents
© John Morris, 1998