The C Preprocessor

Prev: Exercise: Reading From a File
Next: Creating Header Files and
Library Functions



Introduction

The C preprocessor is a program that is executed before the source code is compiled.

C preprocessor commands are called DIRECTIVES, and begin with a pound / hash symbol (#). No white space should appear before the #, and a semi colon is NOT required at the end.

You've already seen a directive: #include

This takes the specified file, and pastes its contents into the place where you put #include before the source code is compiled. So when you use printf, stdio.h is required so that the compiler knows what printf is.



#define

#define allows you to make text substitutions before compiling the program. Here's an example:

#define MAX 10

Before compilation, if the C preprocessor finds MAX as one word (so words like MAXIMUM will not be affected), in the source code, it replaces it with the number 10. If MAX was part of a string (for example, between the quote marks of printf), the preprocessor will leave it alone.

You can call the MACRO DEFINITION anything you want, as long as it doesn't contain special characters or spaces and it cannot start with a number. I tend to use uppercase and underscore characters. You can define strings as well:

#define WEBSITE_NAME "Eddie's Basic Guide to C"

- Every time the preprocessor sees WEBSITE_NAME it will replace it with "Eddie's Basic Guide to C"

#define commands must be placed before the main function. The # symbol must be the first character of the line in the source code.

#include <stdio.h>

#define MIN 0   /* #defines */
#define MAX 10
#define TRUE 1
#define FALSE 0

int main() {     /* beginning of program */
  int a;
  int okay = FALSE; /* the compiler sees this as int okay = 0; */

  while(!okay) {
    printf("Input an integer between %d and %d: ", MIN, MAX);
    scanf("%d", &a);

    if(a>MAX) {
      printf("\nToo large.\n");
    }
    else if(a<MIN) {
      printf("\nToo small.\n");
    }
    else {
      printf("\nThanks.\n");
      okay = TRUE;
    }
  }
  return 0;
}

The program will loop until you enter a number between 0 and 10.

By the time the compiler receives the program, it won't see words like MIN, MAX, TRUE or FALSE in the source code. Instead, it'll see the numbers 0, 10, 1 and 0 respectively.



#undef and Macro Functions

If you've created a macro definition, you can use #undef to remove it. This means the preprocessor will no longer make anymore text substitutions associated with that word. Also, to change a definition, you must use #undef to undefine it, then use #define to redefine it.

You can use #define to create your own MACRO FUNCTIONS. These are useful when the function is relatively small.

For example:

#define SQR(a) (a*a)

Now if you write SQR(3) in your program, the preprocessor will replace it with (3*3) in your program, NOT 9.

Be careful with your brackets, as this example demonstrates:

#include <stdio.h>

#define MAX(a,b) (a>b ? a : b) /* a "function" */
#define DIFF1  4-7
#define DIFF2 (4-7)
#define NUMBER 10

int main() {
  int a=4, b=7;

  printf("Out of %d and %d, %d is the bigger number.\n", 
          a, b, MAX(a,b));
  printf("DIFF1 =  4-7.  DIFF1 times 10 equals %d.\n", DIFF1*10);
  printf("DIFF2 = (4-7). DIFF2 times 10 equals %d.\n", DIFF2*10);

  printf("I live at number %d.\n", NUMBER);
  printf("I'm moving soon...");

#undef NUMBER  
/* now undefine NUMBER so that we can give it a different value */
#define NUMBER 7

  printf(" now I live at number %d.\n", NUMBER);

  return 0;
}

Output:

Out of 4 and 7, 7 is the bigger number.
DIFF1 = 4-7. DIFF1 times 10 equals -66.
DIFF2 = (4-7). DIFF2 times 10 equals -30.
I live at number 10.
I'm moving soon... now I live at number 7.

Let's look at the printf statements:

The compiler will see the first one as:

printf("Out of %d and %d, %d is the bigger number.\n", 
        a, b, (a>b ? a : b)); 

Since we initialized a and b beforehand, it'll work out if a is greater than b, if so, return a from the expression, else return b.

The compiler will see the next two printfs as:

printf("DIFF1 =  4-7.  DIFF1 times 10 equals %d.\n", 4-7*10);
printf("DIFF2 = (4-7). DIFF2 times 10 equals %d.\n", (4-7)*10);

Notice how the preprocessor leaves DIFF1 and DIFF2 alone inside the string of printf. Looking at the above lines, you can clearly see why the top printf prints out -66, where as the bottom did the obvious and printed 30 - macro definitions are just text substitutions!

The next printf is seen as : printf("I live at number %d.\n", 10);

After that, NUMBER is undefined, then redefined, so the next printf will look like:

printf(" now I live at number %d.\n", 7);



#ifdef, #ifndef and #endif

When you come to write big programs, sometimes you'll have to go through a process called DEBUGGING, which involves reading through your program, and trying to find out what's causing your program to go wrong.

One common method for debugging is to comment out sections of code to find that "bug". But what if there are comment dotted around your source code? That's when #ifdef and #ifndef come into play. If you try nesting your comments, you're going to get a compilation error.

In the case of #ifdef, if the macro definition that follows immediately is defined with #define, the following code from that directive to the #endif directive will be considered as part of the program and will be compiled.

With #ifndef, it's the other way round, i.e. if there isn't a definition, the code up to the #endif directive will be considered as part of the program and will be compiled.

Example:

#include <stdio.h>

#define WORD1 
/* you don't have to put an expression after the defined word */

int main() {
#ifdef WORD1
 printf("1: WORD1 is defined so this bit is compiled.\n");
#endif

#ifndef WORD2
 printf("2: WORD2 is not defined so this bit is compiled.\n");
#endif

#undef WORD1
#define WORD2

#ifdef WORD1
 printf("3: WORD1 is now undefined so this bit is not compiled.\n");
#endif

#ifndef WORD2
 printf("4: WORD2 is now defined so this bit is not compiled.\n");
#endif

 return 0;
}

Output:

1: WORD1 is defined so this bit is compiled.
2: WORD2 is not defined so this bit is compiled.

 



Prev: Exercise: Reading From a File
Next: Creating Header Files and
Library Functions

www.iota-six.co.uk Copyright © 2001-2003
Unauthorized copying not permitted

Designed and Developed Using Macromedia Studio MX