C Language The Preprocessor


Example

Before the C compiler starts compiling a source code file, the file is processed in a preprocessing phase. This phase can be done by a separate program or be completely integrated in one executable. In any case, it is invoked automatically by the compiler before compilation proper begins. The preprocessing phase converts your source code into another source code or translation unit by applying textual replacements. You can think of it as a "modified" or "expanded" source code. That expanded source may exist as a real file in the file system, or it may only be stored in memory for a short time before being processed further.

Preprocessor commands start with the pound sign ("#"). There are several preprocessor commands; two of the most important are:

  1. Defines:

    #define is mainly used to define constants. For instance,

    #define BIGNUM 1000000
    int a = BIGNUM; 
    

    becomes

    int a = 1000000;
    

    #define is used in this way so as to avoid having to explicitly write out some constant value in many different places in a source code file. This is important in case you need to change the constant value later on; it's much less bug-prone to change it once, in the #define, than to have to change it in multiple places scattered all over the code.

    Because #define just does advanced search and replace, you can also declare macros. For instance:

    #define ISTRUE(stm) do{stm = stm ? 1 : 0;}while(0)
    // in the function:
    a = x;
    ISTRUE(a);
    

    becomes:

    // in the function:
    a = x;
    do {
        a = a ? 1 : 0;
    } while(0);
    

    At first approximation, this effect is roughly the same as with inline functions, but the preprocessor doesn't provide type checking for #define macros. This is well known to be error-prone and their use necessitates great caution.

    Also note here, that the preprocessor would also replace comments with a blanks as explained below.

  2. Includes:

    #include is used to access function definitions defined outside of a source code file. For instance:

     #include <stdio.h> 
    

    causes the preprocessor to paste the contents of <stdio.h> into the source code file at the location of the #include statement before it gets compiled. #include is almost always used to include header files, which are files which mainly contain function declarations and #define statements. In this case, we use #include in order to be able to use functions such as printf and scanf, whose declarations are located in the file stdio.h. C compilers do not allow you to use a function unless it has previously been declared or defined in that file; #include statements are thus the way to re-use previously-written code in your C programs.

  3. Logic operations:

    #if defined A || defined B
    variable = another_variable + 1;
    #else
    variable = another_variable * 2;
    #endif
    

    will be changed to:

    variable = another_variable + 1;
    

    if A or B were defined somewhere in the project before. If this is not the case, of course the preprocessor will do this:

    variable = another_variable * 2;
    

    This is often used for code, that runs on different systems or compiles on different compilers. Since there are global defines, that are compiler/system specific you can test on those defines and always let the compiler just use the code he will compile for sure.

  4. Comments

    The Preprocessor replaces all comments in the source file by single spaces. Comments are indicated by // up to the end of the line, or a combination of opening /* and closing */ comment brackets.