The preprocessor is a text transformer
The preprocessor doesn't understand C. It doesn't know about types, scopes, or expressions.
It performs text substitutions on your source file before the compiler ever sees it.
Every line starting with # is a directive to the preprocessor, not to the compiler.
Run gcc -E file.c to see what the preprocessor produces.
A short file with a single #include <stdio.h> expands to 800+ lines.
That's what the compiler actually compiles.
#include — file inclusion
#include <stdio.h> is literally replaced by the contents of stdio.h.
Angle brackets search system include paths. Quotes search the current directory first.
#define — macros
#define creates a text substitution. Every occurrence of the name
in the source is replaced with the defined text — before parsing, before type checking.
MAX(x++, y++) increments both arguments twice if x > y.
Use static inline functions instead of function-like macros whenever possible.
static inline int max(int a, int b) { return a > b ? a : b; }
Conditional compilation
#ifdef, #ifndef, #if, #else, #endif
include or exclude blocks of code at compile time.
This is how platform-specific code and debug builds work.
#ifndef FILENAME_H / #define FILENAME_H / ... / #endif.
Or use #pragma once — supported by all modern compilers.
The preprocessor rewrites your source with text substitution before the compiler sees a single token.