CThe Preprocessor

The Preprocessor

Before a single line of your C code is actually compiled into machine instructions, it passes through a separate program called the preprocessor. The preprocessor performs a purely textual transformation of your source file: it deletes comments, pastes in the contents of included files, expands macros, and strips out or keeps blocks of code depending on conditional directives. Only after all of that text-substitution work is done does the real compiler ever see the result.

Why this matters
A lot of confusing compiler errors and "why did this expand that way?" surprises in C trace directly back to preprocessor behavior. Understanding that the preprocessor runs first — and works purely on text, with no idea what a variable, type, or function is — demystifies a huge chunk of beginner C pitfalls.
A Separate Pass, Before Real Compilation

Every directive that starts with a # (like #include, #define, or #if) is an instruction to the preprocessor, not to the C compiler itself. The overall pipeline for a single source file looks like this:

  • .c file with # directives goes in

  • Preprocessor: strips comments, pastes in included files, expands macros, resolves conditional blocks

  • The result is pure C source with no # directives left (mostly) — this is called a translation unit

  • The real compiler parses and compiles that translation unit into object code

Viewing the Preprocessed Output

You do not have to take this on faith — GCC and Clang let you stop the pipeline right after the preprocessing stage and dump the result with the -E flag.

Bash
gcc -E hello.c -o hello.i
# hello.i now contains the fully expanded, macro-substituted,
# comment-free C source that the real compiler would have seen.

C
#define GREETING "Hello, preprocessor!"

#include <stdio.h>

int main(void)
{
    printf("%s\n", GREETING);
    return 0;
}

Run gcc -E on the snippet above and you will see the entire contents of stdio.h pasted in verbatim near the top of the output, followed by your main function with GREETING replaced literally by the string "Hello, preprocessor!". Nothing magical happens — it is a mechanical find-and-replace.

The Main Directive Families

Family

Directives

Purpose

Macros

#define, #undef

Define text substitutions — simple constants or function-like expansions

File inclusion

#include

Paste the contents of another file (usually a header) in place

Conditional compilation

#if, #ifdef, #ifndef, #elif, #else, #endif

Include or exclude blocks of source text based on compile-time conditions

Other

#pragma, #error, #line

Compiler-specific hints, forced compile errors, line-number bookkeeping

Rule of thumb
If a line starts with `#`, it belongs to the preprocessor's world of text substitution — not to the semantic world of C types, scopes, and functions. Keep that mental separation and directives stop feeling mysterious.

The next few pages dig into each directive family in depth, starting with the most commonly used one: macros.