The preprocessor
Before the compiler sees your program, the preprocessor reads it and rewrites it. It doesn't understand C at all: it works on text, following the lines that start with #, called directives. It pastes in header files, replaces names with their values, and keeps or drops whole blocks of code.
| Directive | What it does |
|---|---|
#include | pastes another file in |
#define | defines a name (a macro) to be replaced |
#undef | forgets a macro |
#if, #elif, #else, #endif | keeps or drops code depending on a condition |
#ifdef, #ifndef | the same, depending on whether a macro is defined |
#error | stops compiling with a message |
#pragma | an instruction for one particular compiler |
#include
#include <stdio.h> is replaced by the entire contents of the file stdio.h: thousands of lines of declarations. That's how your program learns that printf exists.
#include <name.h>searches the system's standard folders: use it for the standard library.#include "name.h"looks next to your own file first: use it for your own headers.
A larger program is split into files: shop.c with the code, and shop.h with the declarations other files need (prototypes, #defines, structure types). Every file that uses the shop code includes shop.h.
#define: constants
#include <stdio.h>
#define SEATS 40
#define ROWS 5
int main() {
int booked = 31;
printf("%d of %d seats left\n", SEATS - booked, SEATS);
printf("%d seats in each of %d rows\n", SEATS / ROWS, ROWS);
return 0;
}Every SEATS becomes 40 before compiling, so these names cost nothing at run time. A macro's value can be any text: a decimal (#define GST_RATE 0.18) or a string (#define SHOP_NAME "CodeMIRAI Cafe") work just the same in C. By convention macro names are in CAPITALS, and a #define line has no = and no semicolon: #define SEATS 40; would paste 40; everywhere, semicolon included.
#define or const? A const variable has a type the compiler checks and appears by name in a debugger, so many programmers prefer it for values; #define is still common, and is needed where C insists on a constant, such as an array size in older code.
Macros with parameters
A macro can take arguments, and is replaced by its body with the arguments pasted in:
#include <stdio.h>
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() {
printf("%d\n", SQUARE(7));
printf("%d\n", SQUARE(1 + 2));
printf("%d\n", MAX(12, 30));
return 0;
}It looks like a function call, but it's text substitution, and that has traps.
Trap 1: brackets
Without brackets around each use of the parameter and around the whole body, the substituted text can mean something else:
#include <stdio.h>
#define BAD_SQUARE(x) x * x
int main() {
printf("%d\n", BAD_SQUARE(1 + 2)); // becomes 1 + 2 * 1 + 2
return 0;
}BAD_SQUARE(1 + 2) becomes 1 + 2 * 1 + 2, which is 5, not 9. Always bracket every parameter and the whole body, as SQUARE does.
Trap 2: side effects
An argument used twice in the body is evaluated twice:
#include <stdio.h>
#define SQUARE(x) ((x) * (x))
int main() {
int i = 3;
int r = SQUARE(i++); // becomes ((i++) * (i++))
printf("r = %d, i = %d\n", r, i);
return 0;
}r = 12, i = 5i went up twice, and modifying a variable twice in one expression is undefined behaviour: the result isn't even guaranteed. A real function evaluates its argument once. Prefer functions for anything more than a tiny calculation.
Conditional compilation
#if keeps or drops code before compiling. The classic use is a debug switch: extra checks and messages while you develop, gone from the finished program.
#include <stdio.h>
#define DEBUG 1
int main() {
int total = 0;
for (int i = 1; i <= 3; i++) {
total += i * 10;
#if DEBUG
printf("[debug] i = %d, total = %d\n", i, total);
#endif
}
printf("total = %d\n", total);
return 0;
}[debug] i = 1, total = 10
[debug] i = 2, total = 30
[debug] i = 3, total = 60
total = 60Change DEBUG to 0 and the debug lines aren't just skipped: they're removed before compiling, as if never written. With GCC you can also set a macro from the command line without editing the file: gcc -DDEBUG=1 shop.c.
#ifdef NAME asks whether NAME is defined at all, and #ifndef whether it isn't. Programs use them to adapt to the system they're compiled on:
#ifdef _WIN32
printf("Running on Windows\n");
#else
printf("Running on Linux or macOS\n");
#endif_WIN32 is defined by compilers on Windows, so only one of the two lines is compiled.
Header guards
If two headers both include shop.h, its declarations would be pasted twice, and defining a structure type twice is an error. A header guard makes a header safe to include more than once:
// shop.h
#ifndef SHOP_H
#define SHOP_H
#define MAX_ITEMS 100
double total_price(const double prices[], int n);
#endifThe first time, SHOP_H isn't defined, so the contents are kept and SHOP_H gets defined. Every later time, #ifndef SHOP_H is false and the whole file is skipped. Many compilers also accept #pragma once at the top of a header to do the same in one line.
Predefined macros, # and ##
The preprocessor defines some names itself:
#include <stdio.h>
#define SHOW(expr) printf(#expr " = %d\n", expr)
#define JOIN(a, b) a##b
int main() {
printf("This is line %d\n", __LINE__);
printf("Inside the function %s\n", __func__);
SHOW(6 * 7);
int JOIN(seat, 12) = 3; // declares a variable named seat12
printf("seat12 = %d\n", seat12);
return 0;
}This is line 6
Inside the function main
6 * 7 = 42
seat12 = 3| Name | Gives |
|---|---|
__LINE__ | the current line number |
__FILE__ | the source file's name, as it was given to the compiler (often with its folder) |
__DATE__, __TIME__ | when it was compiled |
__func__ | the current function's name (strictly a variable, not a macro) |
In a macro body, #x turns the argument into a string ("stringizing"), which is how SHOW prints the expression itself, and a##b glues two pieces into one name. Both are handy in debugging helpers, and best used sparingly.
Seeing what the preprocessor did
gcc -E file.c stops after preprocessing and prints the result: every #include expanded, every macro replaced, every dropped #if block gone. When a macro misbehaves, this shows exactly what the compiler received.