The Pre Processor
I've used the #def ine and #include directives without going into detail about what they do. These directives—and others that we haven't yet covered—are handled by the preprocessor, a piece of software that edits C programs just prior to compilation. Its reliance on a preprocessor makes C (along with C++) unique among major programming languages.
The preprocessor is a powerful tool, but it also can be a source of hard-to-find bugs. Moreover, the preprocessor can easily be misused to create programs that are almost impossible to understand. Although some C programmers depend heavily on the preprocessor, I recommend that it—like so many other things in life—be used in moderation.
How the Preprocessor Works
The behavior of the preprocessor is controlled by preprocessing directives: commands that begin with a # character. We've encountered two of these directives, #define and #include. in previous chapters.
The #define directive defines a macro—a name that represents something else, such as a constant or frequently used expression.
The preprocessor responds to a #define directive by storing the name of the macro together with its definition.
When the macro is used later in the program, the preprocessor "expands " the macro, replacing it by its defined value.
The #include directive tells the preprocessor to open a particular file and "include" its contents as part of the file being compiled. For example, the line
instructs the preprocessor to open the file named stdio. h and bring its contents into the program. (Among other things, stdio.h contains prototypes for C's standard input/output functions.)
The following diagram shows the preprocessor's role in the compilation process:
Modified C program
The input to the preprocessor is a C program, possibly containing directives. The preprocessor executes these directives, removing them in the process. The output of the preprocessor is another C program: an edited version of the original program, containing no directives. The preprocessor's output goes directly into the compiler, which checks the program for errors and translates it to object code (machine instructions).
Most preprocessing directives fall into one of three categories:
Macro definition. The #define directive defines a macro; the #undef directive removes a macro definition.
File inclusion. The #include directive causes the contents of a specified file to be included in a program.
Conditional compilation. The #if, #ifdef, #ifndef, #elif, #else, and #endif directives allow blocks of text to be either included in or excluded from a program, depending on conditions that can be tested by the preprocessor.
The remaining directives—#error. #line. and #pragma—are more specialized and therefore used less often. We'll devote the rest of this chapter to an in- depth examination of preprocessing directives. Directives always begin with the #symbol. The # symbol need not be at the be ginning of a line, as long as only white space precedes it. After the # comes the name of the directive, followed by any other information the directive requires.
Any number of spaces and horizontal tab characters may separate the tokens in a directive. For example, the following directive is legal:
# define N 100
Directives always end at the first new-line character, unless explicitly continued. To continue a directive to the next line, we must end the current line with a \ character. For example, the following directive defines a macro that represents the capacity of a hard disk, measured in bytes.
Directives can appear anywhere in a program. Although we usually put #def ine and #include directives at the beginning of a file, other directives are more likely to show up later, even in the middle of function definitions.