The C Preprocessor

The C preprocessor, also used with C++ and optionally with Fortran, is a stream editor specially designed to modify source code. It recognizes language tokens, such as variable names, numeric constants, strings, etc.

Preprocessing was added to Fortran by developers who found the features useful in C. The Fortran preprocessor is based on the C preprocessor and is actually the same program in some cases.

Preprocessor actions are designated by directives, which are indicated by a '#' as the first character on the line. The preprocessor is line-oriented, unlike the C and C++ compilers, which are completely free-format.

#define

The C preprocessor is used to define named constants. The names are simply replaced by their value with a simple text substitution.

The #define directive is followed by an identifier that must follow the same naming conventions as a C variable, i.e. it must begin with a letter or underscore, and subsequent characters can be letters, underscores, or digits.

Note

Constant names defined with #define typically use all capital letters, so that they can be easily distinguished from variables where they are used in statements.
#define PI              3.14159265358979323846
#define RADIUS_PROMPT   "Please enter the radius: "
            

Constants are actually the simplest case of what #define can be used for. It can also be used to define macros, which are discussed in the section called “C Preprocessor Macros”.

#include

The #include directive inserts the contents of another source file into the stream at the point where the #include appears.

The files included are known as header files. The use a file name extension of ".h" for C and ".hpp" for C++. They contain things like constant definitions, type definitions, and prototypes, which may be used by many other source files. Factoring out commonly used code in this way eliminates redundancy and greatly reduces the maintenance cost of the source code.

Suppose we have the following C program:

#include "constants.h"

int     main()

{
    printf("PI = %f and Avogadro's constant is %f.\n", PI, AVOGADRO);
    
    return 0;
}
            

The file "constants.h" contains the following:

#define PI          3.141592653589
#define AVOGADRO    6.02e23
            

The output of the C preprocessor, which is passed on to the compiler, will then be the following:

int     main()

{
    printf("PI = %f and Avogadro's constant is %f.\n", 3.141592653589, 6.02e23);
    
    return 0;
}
            

Caution

It is widely regarded as a very bad practice to place any executable statements in header files. Doing so can lead to the same code being defined in multiple places in the same program. All statements should be in a ".c" file and headers should be used only to define constants, macros, and new data types.

Header files provided by the system or installed globally with libraries have their names enclosed in angle brackets. The preprocessor looks for these headers in /usr/include by default.

For example, to include /usr/include/stdio.h and /usr/include/sys/types.h in your program, simply use the following:

#include <stdio.h>
#include <sys/types.h>
            
cc myprog.c -o myprog
            

The -I flag indicates additional directories to check for headers. The path indicated in #include is relative to the prefix specified with -I.

For example, if you are using vector functions from the GNU Scientific Library (GSL) and the GSL headers are installed in /usr/local/include/gsl, then you could do the following:

#include <gsl/gsl_vector.h>
            
cc -I/usr/local/include myprog.c -o myprog
            

Alternatively, you could use the following:

#include <gsl_vector.h>
            
cc -I/usr/local/include/gsl myprog.c -o myprog
            

Header files that are part of the project and reside in the same directory as the code being compiled are enclosed in double quotes.

#include "constants.h"
            

C++ compilers use the exact same directives and compiler flags. The gfortran compiler will use them as well, as long as it is told to use the preprocessor by specifying -cpp or by using an appropriate filename extension such as ".fpp" or ".F90" instead of ".f90".

Practice

Note

Be sure to thoroughly review the instructions in Section 2, “Practice Problem Instructions” before doing the practice problems below.
  1. How does the C preprocessor differ from other stream editors, such as sed?

  2. What languages use the preprocessor?

  3. Show a preprocessor directive that defines a constant named HOME_PLANET with the value "Earth".

  4. What is the output of the preprocessor for the following code:

    // planets.c
    #include "planets.h"
    
    int     main()
    
    {
        printf("My home planet is %s.\n", HOME_PLANET);
        
        return 0;
    }
    
    // planets.h
    #define HOME_PLANET "Earth"
            
  5. What kind of code should and should not be placed in header files?

  6. Show a command for compiling the following program, prog1.c, to prog1, if stdio.h is a standard header file in /usr/include and xtend/math.h represents /usr/local/include/xtend/math.h.

    #include <stdio.h>
    #include <xtend/math.h>
    
    int     main()
    
    {
        return 0;
    }