194 Programming Exceptions are conditions that can arise when a function does not behave as expected. Exceptions frequently occur when requesting system services such as allocating memory or opening files. Since C provides no exception- handling support, the programmer must add several lines of exception-han- dling code for each service request. For example, this is the way that all of the C textbooks say you are sup- posed to use the malloc() memory allocation function: struct bpt *another_function() { struct bpt *result result = malloc(sizeof(struct bpt)) if (result == 0) { fprintf(stderr, “error: malloc: ???\n”) /* recover gracefully from the error */ [...] return 0 } /* Do something interesting */ [...] return result } The function another_function allocates a structure of type bpt and returns a pointer to the new struct. The code fragment shown allocates memory for the new struct. Since C provides no explicit exception-handling support, the C programmer is forced to write exception handlers for each and every system service request (this is the code in bold). Or not. Many C programmers choose not to be bothered with such triviali- ties and simply omit the exception-handling code. Their programs look like this: struct bpt *another_function() { struct bpt *result=malloc(sizeof(struct bpt)) /* Do something interesting */ return result } It’s simpler, cleaner, and most of the time operating system service requests don’t return errors, right? Thus programs ordinarily appear bug
“It Can’t Be a Bug, My Makefile Depends on It!” 195 free until they are put into extraordinary circumstances, whereupon they mysteriously fail. Lisp implementations usually have real exception-handling systems. The exceptional conditions have names like OUT-OF-MEMORY and the pro- grammer can establish exception handlers for specific types of conditions. These handlers get called automatically when the exceptions are raised— no intervention or special tests are needed on the part of the programmer. When used properly, these handlers lead to more robust software. The programming language CLU also has exception-handling support embedded into the language. Every function definition also has a list of exceptional conditions that could be signaled by that function. Explicit lin- guistic support for exceptions allows the compiler to grumble when excep- tions are not handled. CLU programs tend to be quite robust since CLU programmers spend time thinking about exception-handling in order to get the compiler to shut up. C programs, on the other hand… Date: 16 Dec 88 16:12:13 GMT Subject: Re: GNU Emacs From: debra@alice.UUCP In article 448@myab.se lars@myab.se (Lars Pensj) writes: ...It is of vital importance that all programs on their own check results of system calls (like write).... I agree, but unfortunately very few programs actually do this for read and write. It is very common in Unix utilities to check the result of the open system call and then just assume that writing and closing will go well. Reasons are obvious: programmers are a bit lazy, and the programs become smaller and faster if you don’t check. (So not checking also makes your system look better in benchmarks that use standard utili- ties...) The author goes on to state that, since most Unix utilities don’t check the return codes from write() system calls, it is vitally important for system administrators to make sure that there is free space on all file systems at all time. And it’s true: most Unix programs assume that if they can open a file for writing, they can probably write as many bytes as they need. Things like this should make you go “hmmm.” A really frightening thing about the Miller et al. article “An Empirical Study of the Reliability of Unix Utilities” is that the article immediately preceding it tells about how