Tuesday 1 October 2013

Preprocessor Directives

Introduction and Motivation

  • The first step in compiling any C program is the preprocessor, a sort of automated editor that modifies ( a copy of ) the source code before passing it on to the compiler to translate into machine language code.
  • One of the tasks of the preprocessor is to strip off all comments, which the compiler ignores.
  • The preprocessor also responds to directives in the code, which give the preprocessor explicit instructions on how to edit the source code before passing it on to the compiler.

The #include Preprocessor Directive

  • The #include directive causes the preprocessor to read in the contents of some other file, which may in turn #include some other file(s), etc.
  • See notes on "Writing Large Programs" for full details, including typical contents of #included files and how to avoid circular includes.

The #define Preprocessor Directive

  • The #define directive is used to "define" preprocessor "variables", which can then be used in one of three ways, as shown in the following three sections.
  • It is also possible to define preprocessor variables when invoking the compiler, either as command-line arguments or through the IDE.

Using #define For Boolean Testing

  • Sometimes the preprocessor only needs to know whether a particular preprocessor variable is defined or undefined.
  • In this case, use #define and #undef to turn such variables "on" or "off"
  • The status of a preprocessor variable can be tested with #ifdef or #ifndef, either of which starts a block of code that must be terminated by a matching #endif. ( #elif and #else can also be used, if you need more complicated if-else type blocks. )
  • For example, you may have a number of printing statements that you only want active when debugging. You can "protect" them in a "ifdef" block as follows:
  •  
      #ifdef DEBUG
      
           printf( "Now beginning loop %d with x = %f, y = %f, and z = %f\n", i, x, y, z );
           
      #endif
  • Then you can turn all such blocks on or off at once using
  •   #define DEBUG
  • or
  •   #undef DEBUG
  • Boolean type preprocessor variables can be defined from the UNIX command-line using the "-D" flag to gcc, i.e. "-DDEBUG".

Using #define For Text Substitution

  • The most common use for #define is for automatic substitution of a text string.
  • This was covered in detail, including common pitfalls in the Defined Constants section of Writing Large Programs

Using #define To Define Macros

  • In addition to simple variable substitutions, it is also possible to create pre-processor macros that take arguments.
  • Macros operate much like functions, but because they are expanded in place they are generally faster, since they do not invoke the overhead of transferring control over to a function and then transferring control back again.
  • However there are some potential dangers when using macros instead of functions, as described below.
  • Example:
  •      #define  PI 3.14159
         #define  SQUARE(x)  x * x
         ...
         area  = PI * SQUARE( radius );
  • which will be seen by the compiler as:
  •      area  = 3.14159 * radius * radius;
  • Note:  It is very important to include parentheses around all variables used in a preprocessor macro, as well as around the entire definition.
  • Exercise:  What will be the effects of the following?  Which is correct?:

    1. #define  SQUARE(x)     x * x
      ...
      abSquared = SQUARE( a + b );
    2. #define  SQUARE(x)     (x) * (x)
      ...
      a_over_b_squared = a / SQUARE( b );
    3. #define  SQUARE(x)       ( (x) * (x) )
      ...
      answer = SQUARE( a + b ) / SQUARE( b );
  • Landmine!  Do not use auto-increment or decrement operators, assignment statements, or function calls in combination with preprocessor macros.  Consider:
  •      for( i = 0; i < 10;  )
              printf( "%d squared = %d\n, i, SQUARE( i++ )  );
  • Preprocessor macros can take multiple arguments:
  •      #define A_MOD_B(a,b)    ( (a) % (b) )
  • Preprocessor macros can also make use of previously defined macros
  •      #define MAX(a,b)    ( (a) > (b) ? (a) : (b) )
         #define MAX3(a,b,c)    ( ((a) > (b)) ? MAX((a),(c)) : MAX((b),(c)) )
         // or #define MAX3(a,b,c)    ( MAX((a), MAX((b),(c)) )
  • Preprocessor macros can span multiple lines only if the end of line is explicitly escaped:
  •      #define MIN(a,b)    ( (a) < (b) ? \
              (a) : \
              (b) )
  • Note that macros do not check the types of their arguments, which can have both good and bad consequences.

Predefined Macros

  • There are a number of macros automatically pre-defined by the compiler, inlcuding the following list. Note that each one begins and ends with TWO underscore characters:
    • __LINE__ - The line number of the current file being compiled
    • __FILE__ - The name of the current file being compiled
    • __DATE__ - The current date
    • __TIME__ - The current time

The #pragma Preprocessor Directive

  • The #pragma directive is used to give the preprocessor ( and compiler ) specific details on exactly how to compile the program. For example, specific compiler warnings can be ignored, or warning levels changed up or down for different sections of code.
  • Examples:
    • #pragma page( ) // Forces a form feed in the listing
    • #pragma warning( disable: 2044 ) // Disables warning number 2044
    • #pragma line 100 // Sets the current line number to 100 for listing and reporting purposes.

No comments:

Post a Comment