next up previous contents
Next: Compiler Options Up: Compiling Previous: Compiler Input and Output   Contents

Preprocessing

Strictly speaking compiling involves two distinct processes: preprocessing and then compiling. Preprocessing involves the interpretation of preprocessor directives. These start with the # character and modify the source that has to be compiled. Some of the most important are:-

#include

include inserts the contents of a specified file (often called a header file) at the current point in the file. For example:-

  #include "MyPackage/MyClass.h"
  #include <iostream>

The preprocessor looks for the named file in the directory containing the file that is being compiled, in the first example above it will look in this directory for the directory MyPackage and in that directory for the file MyClass.h. If the search fails it will search in other directories as well. In the second example above the angle brackets means that this is a standard header so the preprocessor must look in standard system directories for the file iostream.

#define

This define a macro, a symbol that will subsequently be replaced by its associated value. For example:-

  #define MAXCOUNT  100
...
  if ( count >= MAXCOUNT ) return;
The symbol MAXCOUNT is defined to have the associated value of the string 100. Wherever the symbol MAXCOUNT appears following is definition it will be replaced by 100.

Macros can take formal arguments which are replaced by the actual arguments when the macro is used. For example:-

  #define  PRINT(x,y)  cout << (x) << (y)  << endl;
...
  PRINT(number1,number2)
generates the code:-
  cout << (number1) << (number2) << endl;

Aside: macros taking arguments are generally considered to be bad practice because the compiler is passed code that can be quite different to that in the source file. Furthermore it's easy to write macros that don't work as intended and produce syntactically correct but semantically flawed code. They are particularly deprecated in C++ as the language almost invariably provides safer and more powerful alternatives.

#ifdef #ifndef and #if

These directives can be used to get the preprocessor to skip lines of source to the corresponding #endif. A very common application of #ifdef ensures headers are not included multiple times. The reason that it is necessary to guard against this is because header files can be nested i.e. can contain #include directives. If a file includes several headers that include, either directly or indirectly, some common header, then it will be included multiple times unless steps are taken to prevent it.

For example suppose we have the header file MyClass.h. It could start:-

  #ifndef MYCLASS_H
  #define MYCLASS_H
 ..
  #endif

This tells the preprocessor that if the macro MYCLASS_H is not defined, define it and include the lines up to #endif, otherwise skip directly to #endif.

Then when the directive:-

  #include "MyClass.h"

is processed, assuming the MYCLASS_H macro is not defined elsewhere, the macro will be defined (the associated value is a null string) and the remainder of the file is included. However when next included the #ifndef fails and the file is skipped.

The #ifeq allows for expression testing. There are also #elif and #else directives for example:-

#if OPT==4
#include "header4.h"
#elif OPT>0
#include "someheader.h"
#else
#include<cstlib>
#endif

Its also possible to nest conditional blocks.

Preprocessor directives appear in almost all C/C++ code. They can also be used in Fortran so long as the compiler supports preprocessing, but not all compilers do.


next up previous contents
Next: Compiler Options Up: Compiling Previous: Compiler Input and Output   Contents
P.D. Gronbech (IT Staff) 2015-10-02