Preprocessors
The C preprocessor executes before a program is compiled. Some actions it performs are the inclusion of other files in the file being compiled, definition of symbolic constants and macros, a conditional compilation of program code and conditional execution of preprocessor directives.
Preprocessor directives begin with # and only white-space characters and comments may appear before a preprocessor directive on a line.
#include Prepocessor Directive
The #include preprocessor directive has been used throughout this text. The #include directive causes a copy of a specified file to be included in place of the directive.
The two forms of the #include directive are:
#include <filename>
#include "filename"
The difference between these is the location the preprocessor begins searches for the file to be included. If the file name is enclosed in quotes, the preprocessor starts searches in the same directory as the file being compiled for the file to be included (and may search other locations as well). This method is normally used to include programmer-defined headers.
If the file name is enclosed in angle brackets (< and >) used for standard library headers the search is performed in an implementation-dependent manner, normally through pre-designated compiler and system directories.
The #include directive is used to include standard library headers such as stdio.h and stdlib.h and with programs consisting of several source files that are to be compiled together. A header containing declarations common to the separate program files is often created and included in the file. Examples of such declarations are structure and union declarations, enumerations and function prototypes.
#define Preprocessor Directive: Symbolic Constants
The #define directive creates symbolic constants, constants represented as symbols and macros operations defined as symbols. The #define directive format is
#define identifier replacement-text
When this line appears in a file, all subsequent occurrences of identifier that do not appear in string literals will be replaced by replacement-text automatically before the program is compiled.
For example,
#define PI 3.14159
replaces all subsequent occurrences of the symbolic constant PI with the numeric constant 3.14159. Symbolic constants enable you to create a name for a constant and use the name throughout the program. If the constant needs to be modified throughout the program, it can be modified once in the #define directive. When the program is recompiled, all occurrences of the constant in the program will be modified accordingly. [Note: Everything to the right of the symbolic constant name replaces the symbolic constant.]
For example,
#define PI = 3.14159 causes the preprocessor to replace every occurrence of the identifier
PI with = 3.14159.
This is the cause of many subtle logic and syntax errors. Redefining a symbolic constant with a new value is also an error.
#define Preprocessor Directive: Macros
A macro is an identifier defined in a #define preprocessor directive. As with symbolic constants, the macro-identifier is replaced in the program with the replacement-text before the program is compiled. Macros may be defined with or without arguments. A macro without arguments is processed like a symbolic constant. In a macro with arguments, the arguments are substituted in the replacement text, then the macro is expanded i.e., the replacement-text replaces the identifier and argument list in the program.
Consider the following macro definition with one argument for the area of a circle:
#define CIRCLE_AREA( x ) ( ( PI ) * ( x ) * ( x ) )
Wherever CIRCLE_AREA(y) appears in the file, the value of y is substituted for x in the replacement-text, the symbolic constant PI is replaced by its value (defined previously) and the macro is expanded in the program.
For example, the statement
area = CIRCLE_AREA( 4 );
is expanded to
area = ( ( 3.14159 ) * ( 4 ) * ( 4 ) );
and the value of the expression is evaluated and assigned to variable area. The parentheses around each x in the replacement text force the proper order of evaluation when the macro argument is an expression.
For example, the statement
area = CIRCLE_AREA( c + 2 );
is expanded to
area = ( ( 3.14159 ) * ( c + 2 ) * ( c + 2 ) );
which evaluates correctly because the parentheses force the proper order of evaluation. If the parentheses are omitted, the macro expansion is
area = 3.14159 * c + 2 * c + 2;
which evaluates incorrectly as
area = ( 3.14159 * c ) + ( 2 * c ) + 2;
because of the rules of operator precedence.
Forgetting to enclose macro arguments in parentheses in the replacement text can lead to logic errors.
Macro CIRCLE_AREA could be defined as a function.
Function circleArea
double circleArea( double x )
{
return 3.14159 * x * x;
}
performs the same calculation as macro CIRCLE_AREA, but the overhead of a function call is associated with function circleArea. The advantages of macro CIRCLE_AREA are that macros insert code directly in the program avoiding function call overhead and the program remains readable because the CIRCLE_AREA calculation is defined separately and named meaningfully. A disadvantage is that its argument is evaluated twice.
Macros can sometimes be used to replace a function call with an inline code to eliminate the overhead of a function call. Today’s optimizing compilers often inline functions for you, so many programmers no longer use macros for this purpose
The following is a macro definition with two arguments for the area of a rectangle:
#define RECTANGLE_AREA( x, y ) ( ( x ) * ( y ) )
Wherever RECTANGLE_AREA(x, y) appears in the program, the values of x and y are substituted in the macro replacement text and the macro is expanded in place of the macro name. For example, the statement
Symbolic constants and macros can be discarded by using the #undef preprocessor directive. Directive #undef “undefines” a symbolic constant or macro name. The scope of a symbolic constant or macro is from its definition until it is undefined with #undef, or until the end of the file. Once undefined, a name can be redefined with #define. Functions in the standard library sometimes are defined as macros based on other library functions.
A macro commonly defined in the stdio.h header is
#define getchar() getc( stdin )
The macro definition of getchar uses function getc to get one character from the standard input stream. Function putchar of the stdio.h header and the character handling functions of the ctype.h header often is implemented as macros as well. Expressions with side effects (i.e., variable values are modified) should not be passed to a macro because macro arguments may be evaluated more than once.