Basics of C Preprocessors

The Preprocessor Directive

The preprocessor is a program that is invoked by the compiler to process code before compilation. Commands for that program, known as directives, are lines of the source file beginning with the character #, which distinguishes them from lines of source program text. The effect of each preprocessor directive is a change to the text of the source code, and the result is a new source code file, which does not contain the directives. The preprocessed source code, an intermediate file, must be a valid C or C++ program because it becomes the input to the compiler.

Preprocessor directives consist of the following:

  • Macro definition directives, which replace tokens in the current file with specified replacement tokens.
  • File inclusion directives, which imbed files within the current file.
  • Conditional compilation directives, which conditionally compile sections of the current file.
  • Message generation directives, which control the generation of diagnostic messages.

Macro definition directives

Macro definition directives include the following directives and operators:

  • The #define directive, which defines a macro
  • The #undef directive, which removes a macro definition

The #define directive

A Tpreprocessor define directiveT directs the preprocessor to replace all subsequent occurrences of a macro with specified replacement tokens.

The T#defineT directive can contain:

  • Object-like macros
  • Function-like macros

Object like Macros

An object-like macro definition replaces a single identifier with the specified replacement tokens.The following object-like definition causes the preprocessor to replace all subsequent instances of the identifier COUNT with the constant 1000.

#define COUNT 1000

Function-like macros

More complex than object-like macros, a function-like macro definition declares the names of formal parameters within parentheses, separated by commas. An empty formal parameter list is legal: such a macro can be used to simulate a function that takes no arguments.

#define SUM(a,b) (a + b)

This definition would cause the preprocessor to change the following statements (if the statements appear after the previous definition):

c = SUM(x,y);
c = d * SUM(x,y);

In the output of the preprocessor, these statements would appear as:

c = (x + y);
c = d * (x + y);

Use parentheses to ensure correct evaluation of replacement text.

#undef directive

A Tpreprocessor undef directiveT causes the preprocessor to end the scope of a preprocessor definition. If the identifier is not currently defined as a macro, T#undefT is ignored.The following directives define TBUFFERT and TSQRT:

#define BUFFER 512
#define SQR(x) ((x) * (x))

The following directives nullify these definitions:

#undef BUFFER
#undef SQR

File inclusion directives

The #include directive allows external header files to be processed by the compiler.Syntax:

#include <header-file>

Or

#include "source-file"

When enclosing the file with, then the implementation searches the known header directories for the file (which is implementation-defined) and processes it. When enclosed with double quotation marks, then the entire contents of the source-file is replaced at this point. The searching manner for the file is implementation-specific.

Examples:

#include <stdio.h>
#include "my_header.h"

Conditional compilation directive

It causes the preprocessor to conditionally suppress the compilation of portions of source code.These directives test a constant expression or an identifier to determine which tokens the preprocessor should pass on to the compiler and which tokens should be bypassed during preprocessing. The directives are:

  • The #if and #elif directives, which conditionally include or suppress portions of source code, depending on the result of a constant expression.
  • The #ifdef directive, which conditionally includes source text if a macro name is defined
  • The #ifndef directive, which conditionally includes source text if a macro name is not defined.
  • The #else directive, which conditionally includes source text if the previous T#ifT, T#ifdefT, T#ifndefT, or T#elifT test fails.
  • The #endif directive, which ends conditional text.

The #if and #elif directives

The T#ifT and T#elifT directives compare the value of Tconstant_expressionT to zero. If the constant expression evaluates to a nonzero value, the lines of code that immediately follow the condition are passed on to the compiler.

If the expression evaluates to zero and the conditional compilation directive contains a preprocessor T#elifT directive, the source text located between the T#elifT and the next T#elifT or preprocessor T#elseT directive is selected by the preprocessor to be passed on to the compiler. The T#elifT directive cannot appear after the preprocessor T#elseT directive.

#if OS==1
    printf("Version 1.0");
#elif OS==2
    printf("Version 2.0");
#else
    printf("Version unknown");
#endif

Prints according to the setting of OS which is defined with a #define.

The #ifdef directive

The T#ifdefT directive checks for the existence of macro definitions. If the identifier specified is defined as a macro, the lines of code that immediately follow the condition are passed on to the compiler.

The following example defines TMAX_LENT to be T75T if TEXTENDEDT is defined for the preprocessor. Otherwise, TMAX_LENT is defined to be T50T.

#ifdef EXTENDED
# define MAX_LEN 75
#else
# define MAX_LEN 50
#endif

The #ifndef directive

The T#ifndefT directive checks whether a macro is not defined. If the identifier specified is not defined as a macro, the lines of code immediately follow the condition are passed on to the compiler. An identifier must follow the T#ifndefT keyword. The following example defines TMAX_LENT to be T50T if TEXTENDEDT is not defined for the preprocessor. Otherwise, TMAX_LENT is defined to be T75T.

#ifndef EXTENDED
# define MAX_LEN 50
#else
# define MAX_LEN 75
#endif

The #else directive

If the condition specified in the T#ifT, T#ifdefT, or T#ifndefT directive evaluates to T0T, and the conditional compilation directive contains a preprocessor T#elseT directive, the lines of code located between the preprocessor T#elseT directive and the preprocessor T#endifT directive is selected by the preprocessor to be passed on to the compiler.

The #endif directive

The preprocessor T#endifT directive ends the conditional compilation directive.

/**
 ** This example contains preprocessor
 ** conditional compilation directives.
 **/
#include <stdio.h>
#define TEST 2

int main(void)
{
   static int array[ ] = { 1, 2, 3, 4, 5 };
   int i;
 
   for (i = 0; i <= 4; i++)
   {
      array[i] *= 2;

#if TEST >= 1
   printf("i = %d\n", i);
   printf("array[i] = %d\n",
   array[i]);
#endif

   }
   return(0);
}

O/p:

i = 0
array[i] = 2
i = 1
array[i] = 4
i = 2
array[i] = 6
i = 3
array[i] = 8
i = 4
array[i] = 10

Message generation directives includes the #error directive, which defines text for a compile-time error message.

The #error directive

A Tpreprocessor error directiveT causes the preprocessor to generate an error message and causes the compilation to fail. For example, the directive.

#define BUFFER_SIZE 255

#if BUFFER_SIZE < 256
#error "BUFFER_SIZE is too small."
#endif

generates the error message:

BUFFER_SIZE is too small
Related Post