Preprocessor Directives

The C pre-processor is a special program that runs before the C compiler. It processes every line that begins with a #, such as a #include statement. The pre-processor may add, remove, or change your code when handling the # statements.

#include

We’ve been using the #include statement since our first program in order to get access to C’s library functions (like printf in stdio.h). When the pre-processor sees a #include statement, it replaces the #include line with the contents of the included file. For example, when the pre-processor sees:

#include <stdio.h>

then it replaces that line with the contents of the stdio.h file (which includes definitions for printf, scanf, fprintf, etc.). Then, when the compiler sees a call to printf, it knows what you mean because printf is defined in your file.

Including a file like:

#include <stdio.h>

tells the pre-processor to look for stdio.h in the standard include directory (which is where all the library files live). If instead you say:

#include "stdio.h"

the pre-processor will first look for stdio.h in the current directory. If it can’t find the file, it will hen look in the standard include directory.

#define

The #define statement tells the pre-processor to find all occurrences of one value in your code, and to replace them with another value. This can be used in two ways: to define constants and to define macros (simplified functions).

Constants

A constant in C is defined like this:

#define name value

Here, name is the name we are giving the constant, and value is the value we would like it to have. Here is an example:

#define PI 3.14159

Now, we can use PI in our code when we want the value 3.14159. For example:

int radius = 10;
double area = PI * radius * radius;

When the pre-processor sees a #define constant, it replaces all occurrences of the constant’s name with it’s specified value. So by the time the pre-processor gets done with it, the above code looks like:

int radius = 10;
double area = 3.14159 * radius * radius;

Macros

A macro is simplified function that includes a name and a list of arguments. They are defined using the #define statement, just like constants. For example:

#define SUM(a, b) a+b

When the preprocessor sees a call to the macro, it will replace the macro call with the macro formula. For example, if we do:

int x = 3;
int y = 4;
int result = SUM(x, y);

Then the pre-processor will change the last line to be:

//Uses x as a and y as b in the macro formula
int result = x + y; 

Just like other pre-processor directives, the compiler never sees the macro itself. It only sees the final result, after the pre-processor has replaced the macro call with the macro formula.

As another example, let’s try to write a macro that computes the difference of two squares:

//This is not right!
#define DIFF_SQUARE(a, b) a*a – b*b 

This macro may look right (and this is how we would write a diffSquare function), but it has some problems. For example:

int x = 4;
int y = 3;
int c = 2;
int d = 1;
int result = DIFF_SQUARE(x-c, y-d);

The pre-processor will replace the call to DIFF_SQUARE with the macro value. It will use x-c as the value for a, and y-d as the value for b. Here’s what the last line will become:

int result = x-c*x-c  y-d*y-d;

However, what we WANT is:

int result = (x-c)*(x-c)  (y-d)*(y-d);

To get this substitution, we need to add parentheses to the original macro:

#define DIFF_SQUARE(a, b) (a)*(a) – (b)*(b)

Here is a macro that returns the minimum of two numbers:

#define MIN(a, b) a < b ? a : b

This uses the ternary conditional operator. It means: if a is less then b, then a is the minimum. Otherwise, b is the minimum.

Here is a macro that eats all input left in the input buffer. This can be VERY useful as any unexpected input will stay in the input buffer. It reads one character at a time until it runs out of input.

#define FLUSH() while (getchar() != '\n')

#ifdef, #ifndef

There is also a pre-processor if-statement that checks to see whether or not a constant has been defined. Here’s the format:

#ifdef (pre-processor constant)
code
#endif

The code inside this #ifdef/#endif block will only be compiled if the pre-processor constant was defined. For example:

#define SIZE 10

//(elsewhere)
#ifdef SIZE
int nums[SIZE];
#endif

In this example, we only include the int nums[SIZE]; line in our program if the SIZE constant has already been defined.

There is a similar construct called #ifndef, which evaluates to true if a constant has NOT been defined. For example:

#ifndef SIZE
printf("Define SIZE!\n");
#endif

The printf statement will only be part of the program if the SIZE constant has not been defined. The #ifndef statement will be very useful when we start to break our programs into several files – it will help ensure that we don’t end up including the same information twice when we link our files together.