Preprocessing

A C program can consist of a macros. These macros are expanded by the preprocessor. The preprocessor simply expands the value of the macro in the place of its occurrence. The preprocessor does not perform any validations. Any syntactic error in macros would be reported by the compiler at later stage.

The C preprocessor performs the following four main functions:

    1. Inclusion of header files : These are files of declarations that can be substituted into your program.
    2. Macro expansion : You can define macros, which are abbreviations for arbitrary fragments of C code, and then the C preprocessor will replace the macros with their definitions throughout the program.
    3. Conditional compilation : Using special preprocessing directives, you can include or exclude parts of the program according to various conditions.
    4. Line control : If you use a program to combine or rearrange source files into an intermediate file which is then compiled, you can use line control to inform the compiler of where each source line originally came from.

Consider the following program:

#include <stdio.h>

/* Macros */

#define BEGIN {

#define END }

#define AREA(x) (x*x)

int main(void)

BEGIN

int side=3;

printf("Area of Square with side %d is %d",

side, AREA(side));

return(0);

END

In the above example three macros (BEGIN, END and AREA) have been defined. Assuming that the file containing the above program is area.c, the preprocessed file for the above can be obtained through

$ cpp area.c

"cpp" displays its output on the standard output device (which is monitor in most of the cases). The output obtained is as follows:

# 1 "/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h" 1 3
# 199 "/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h" 3
typedef unsigned int size_t;
# 35 "/usr/include/stdio.h" 2 3
# 1 "/usr/include/bits/types.h" 1 3
# 26 "/usr/include/bits/types.h" 3
.
.
.
#include <stdio.h>
int main(void)
{

int side=3;

printf("Area of Square with side %d is %d",

side, (side*side));

return(0);

}

By convention the preprocessed files are stored in files with extension ".i" for C programs and ".ii" for C++ programs. Therefore the output of "cpp" can be stored as

$ cpp area.c > area.i

Compile Time Macros

In the above example the macros were defined in the source code. However, macros can also be defined during compile time at the command prompt using the "-D< macroname >" option with both cpp and gcc.

Consider the example


#include <stdio.h>

/* Macros */

#define BEGIN {

#define END }

#define AREA(x) (x*x)

int main(void)

BEGIN

int side=3;

#ifdef DEBUG_MODE

printf(" Calculating the area of square of side %d ",

side);

#endif

printf("Area of Square with side %d is %d",

side, AREA(side));

return(0);

END

If the macro DEBUG_MODE is defined then the pre-processor would include the code between #ifdef and #endif in the pre-processed code. This can be done as follows:

$ cpp -DDEBUG_MODE area.c

The corresponding code would be generated

.

.

.

int main(void)

{

int side=3;

printf(" Calculating the area of square of side %d ",

side);

printf("Area of Square with side %d is %d",

side, (side*side));

return(0);

}

.

.

.

This technique is pretty useful when code such as printfs have to be put in several places in code while debugging but not in the final code. Therefore this code code can be put under #ifdef ... #endif and while compiling the final product this macro is not defined. This removes the overhead of debugging code in the final release.

Compiler Provided macros

Other than the user defined macros, compiler also provides some macros for identifying the platform, compiler etc. These macros can be obatined using the -dM option with cpp

$ cpp -dM area.c

#define __attribute__(xyz)

#define __restrict

#define __USER_LABEL_PREFIX__

#define _IO_pos_t _G_fpos_t

#define _IO_DONT_CLOSE 0100000

#define __USE_POSIX199506 1

#define _G_ssize_t __ssize_t

#define _IO_peekc(_fp) _IO_peekc_unlocked (_fp)

#define __STRING(x) #x

.

.

.

#define BEGIN {

.

.