Skip to content

Preprocessor Directives

The preprocessor directives in C starts with the character #. These directives tells the compiler what to do. There are three common C preprocessors:

  1. Header files inclusion.
  2. Macro expansions.
  3. Conditional compilations.

We will focus on the first two as they are given in the example.

Header Files Inclusion

Our example uses #include <stdio.h>. This tells the compiler that we want to include the functions declared in the file stdio.c with the definition mentioned in the file stdio.h. stdio is the standard header file containing many I/O functions (hence called standard IO, or stdio).

The file extension .c is for C program and .h is for the header files. The header files contain only the function prototypes (i.e., return type, function name and parameter types) without the function body (i.e., the actual code).

These header files typically belongs to a library. Some of the common libraries are:

  • #include <math.h>: This contains mathematical functions. In certain older systems, you need to compile with -lm flag (e.g., gcc my_file.c -lm).
  • #include <string.h>: This contains string functions. Remember, the primitive data type is char and a string is an array of char.

Macro Expansions

Our example uses #define KMS_PER_MILE 1.609. One common convention is to use uppercase (i.e., ALL CAPS) for constants defined as macro.

This tells the compiler that any mention of the lexicon KMS_PER_MILE should be replaced with 1.609. In short, it works similar to "find all and replace". This is the standard way to create a constant in C.

Simple Macro Expansion

MacroExpansion.c
1
2
3
4
5
6
#define PI 3.142
int main(void) {
    :
  areaCircle = PI * radius * radius;
  volCome = PI * radius * radius * height / 3.0;
}
MacroExpansion.c
1
2
3
4
5
6
// the define is removed!
int main(void) {
    :
  areaCircle = 3.142 * radius * radius;
  volCome = 3.142 * radius * radius * height / 3.0;
}

So, the preprocessor replaces all instances of the macro before passing the program to the compiler. Furthermore, this expansion is done until there are no more macros to be expanded. A more complicated example is the following.

Complex Macro Expansion

ComplexMacro.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>
#define D 2
#define C D
#define B C + D
#define A B + C

int main(void) {
  int count = A;
  printf("%d\n", count);
  return 0;
}
ComplexMacro.c
1
2
3
4
5
6
7
#include <stdio.h>

int main(void) {
  int count = 2 + 2 + 2;
  printf("%d\n", count);
  return 0;
}

Lexeme

Note that it works on the lexeme level and not character level. As such, if there is KMS_PER_MILE1 in the program, it will not be replaced with 1.6091.

Quick Quiz

Can you try to expand the macro in ComplexMacro.c by hand?

1
2
3
4
5
A
 B + C      // using A ⇝ B + C
 C + D + C  // using B ⇝ C + D
 D + D + D  // using C ⇝ D
 2 + 2 + 2  // using D ⇝ 2

Exercises

Exercise

In the series The Simpsons, Bart can be seen writing on blackboard in many episodes. The first of which is on January 14, 1990 in the episode called "Bart the Genius" He was asked to write:

I will not waste chalk.

Using macro expansion and no loops, can you help Bart to print (_using printf("I will not waste chalk.\n")) 1000 times? Hint: 100 is exactly 103

The basic idea here is to have a single macro expanded 10 times. If this macro is then expanded another 10 times, we would have expanded the original macro 100 times. Another iteration will give us 1000 times.

I will not waste chalk.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>
#define A B B B B B B B B B B
#define B C C C C C C C C C C
#define C D D D D D D D D D D
#define D printf("I will not waste chalk.\n");

int main(void) {
  A
  return 0;
}