First Next Previous Last Glossary About

The C++ Preprocessor


Introduction

Whenever a C++ program is compiled it is processed by the preprocessor which looks for lines which begin with the # character. Note that the preprocessing takes place at compile time, there is no preprocessing at runtime. The C++ language provides several useful facilities which the preprocessor handles:

Include files.
Files which can contain variable declarations, functions and other include files.
Constant definitions.
Definitions for symbolic constants.
Conditional compilation.
Directives to control what does and does not get compiled.
Macros.
Definitions of code that will be expanded.

Return to top of page

Include files

Include files

Replace the line with the file. If filename is in ".." (double quotes) then the current directory is searched first then some standard directories. If filename is in <..> then only the standard directories are searched.

Many of C++'s I/O, maths, system etc functions are defined in include files. These are generally header files (type h files) and need to be included with programs if the definitions are to be used.

#include filename e.g
#include "myfile.cc"
#include <iostream>
#include <ctype.h>
 

We can leave the .h off standard include files since the practice of having two include files, one with .h and one without, seems to be developing.

The iostream file itself includes iostream.h.

Have a look in your include directories to confirm what is there.

You might like to read the topic Header Files: How and why to use header files, in the gcc references. You will find lots of useful information here but it might be heavy going at the moment.

Return to top of page

Constant definitions

Constant definitions

Anywhere that the identifier appears it will be replaced by its token.

#define  identifier token
 

e.g.

#define  AGE           27
#define  MODE        0644
 

Return to top of page

Conditional compilation

Conditional compilation

Direct the compiler to include or exclude parts of the program according to the state of certain conditions.

#if   constant_expression
      include this
#else
      include this instead
#endif

Tests the value of the constant expression and acts accordingly

#if  X > 10
      cout << X;
#else
      count << (X/2);
#endif

If FRED is defined then

#define FRED
ifdef FRED
               ....
#endif
 

if FRED is not defined

#ifndef FRED
               ...
#endif
 

An identifier can be removed from the list of defined identifiers

  #undef FRED
 

If you want to know more you will find the gcc conditionals guide here Conditionals: How and why to use conditionals.

Return to top of page

Macro expansions

Macro expansions

A macro can be defined and can have parameters

Any where that the macro appears in the program it will be expanded to its string.

#define ident(args)    token_string

#define MAX(A,B)  ((A) > (B) ? (A) : (B))
#define SUM(A,B)  (A + B)

eg: x = MAX(12,92);

will expand to:


z = 12;
y = 92;
x = ((y) > (z) ? (y) : (z));

The statement x = ((y) > (z) ? (y) : (z)); might puzzle you. It is a shorthand notation for:


 if (y > x)
  x = y;
 else x = z;

More Macros and Defines - Some examples

Macro substitutions (with arguments):

   #define name(arg1, arg2, ...)  string

Each occurence of name will be replaced by string. NB: no space between macro name and the parenthesis (. The macro name ends at the first whitespace.

You should understand that a macro is an expansion. It is not the same as a function. When the compiler encounters a macro as an executable statement or part of an executable statement it will expand it in place. A function of course is not expanded but is a single section of program that can be called repeatedly.


//ex20.cc
#include <iostream>

#define SQUARE(x) x * x
#define LIMIT     5

int main()
{
  int count = 1;
  char c;
  while (count <= LIMIT)
   { cout << count
          << "\t"
          << SQUARE(count)
          << endl;
     count++;
   }
  return 0;
}
       OUTPUT
     1       1
     2       4
     3       9
     4      16
     5      25

This exercise shows a macro called SQUARE which obviously expands to give the square if its argument.



//ex21.cc
#include <iostream>

#define SQUARE(x)  x * x
#define LIMIT      5

int main()
{
  int count = 2, j = 3;
  cout << count
       << "\t"
       << SQUARE(count + j)
       << endl;  
  return 0;
}

which gives the output:

2       11

ex21 has a problem which is demonstrated here. When the macro expansion in ex21 takes place: SQUARE(2 + 3) it occurs as:

           2 + 3 * 2 + 3

and since * has higher precedence than + the macro evaluates 2 + 6 + 3. Parentheses should be used to ensure that the evaluation takes place in the order required,eg SQUARE(x) (x) * (x)

However even this is not completely safe since a macro may be used in situations where other operators with higher precedence are used.

Macro's should be totally enclosed in parentheses:

   SQUARE(x) ((x) * (x))

//ex22.cc
#include <iostream>

#define SQUARE(x)   ((x) * (x))
#define LIMIT       5

int main()
{
  int count = 2, j = 3;

  cout << count
       << "\t"
       << (count + j)
       << "\t"
       << SQUARE(count + j)
       << endl;  

  return 0;
}

ex21 shows the SQUARE macro as you should write. Use parentheses to force the evaluation you require.



//ex23.cc
#include <iostream>

int main()
{ int y;

  cout << "Type a 4 digit year , eg 1950: ";
  cin>> y;

  if (y % 4 == 0 && 
      y % 100 != 0 || y % 400 == 0)
   cout << y
        << " is a leap year\n";
  else cout << y
            << " is not a leap year\n";
  return 0;
}

The use of macros and defines can significantly assist the readability of a program. This example (ex23) doesn't use defines or macros and could be a little unclear. What on Earth does that long if condition do?



//ex24.cc
#include <iostream>

#define IS_LEAP_YEAR(year) (year %   4 == 0 && \
                            year % 100 != 0 || \
                            year % 400 == 0)
#define then

int main()
{ int y;

  cout << "Type a 4 digit year , eg 1950: ";
  cin >> y;

  if (IS_LEAP_YEAR(y))
   then cout << y
             << " is a leap year\n";
   else cout << y
             << " is not a leap year\n";

  return 0;
}

The next example (ex24) clarifies ex23 a little. It uses a macro and a trivial define. The macro has a meaningful name IS_LEAP_YEAR. The \ at the end of the lines in the macro is a line continuation character. This is necessary if the expansion is all on one line but does it help the readability of the macro if it is spread over several lines. The preprocessor requires its directives one per line, the continuation character is a way of writing "virtual" lines.



#include <iostream>

#define MOD              %
#define EQUALS           ==
#define AND              &&
#define OR               ||
#define NOTEQUALS        !=
#define BECOMES          =
#define LESSTHAN         <
#define INTEGER          int
#define CHARACTER        char
#define LEAPYEAR         " is a leap year\n"
#define NOTLEAPYEAR      " is not a leap year\n"
#define then
#define INCREMENT(x)     (x++)
#define SENDBACK         return
#define BEGIN            {
#define END              }

#define IS_LEAP_YEAR(year) ( year MOD   4 EQUALS    0\
                             AND \
                             year MOD 100 NOTEQUALS 0\
                             OR \
                             year MOD 400 EQUALS    0 )

#define THISYEAR 1993

CHARACTER main()

BEGIN
  INTEGER   y BECOMES THISYEAR - 101;
  CHARACTER c;

  while (y LESSTHAN THISYEAR)
   BEGIN 
    if (IS_LEAP_YEAR(y))
     then cout << y << LEAPYEAR;
     else cout << y << NOTLEAPYEAR;
    INCREMENT(y);
   END
  SENDBACK c;
END

The last example (ex25) shows that you can use symbolic constants and macros to gain some interesting variations on the words used in C++.

You will find the gcc macro guide here Macros: How and why to use macros. It might be another helpful reference.


Return to top of page


Tutorial


First Next Previous Last Glossary About


Copyright © 1999 - 2001 David Beech