Intermediate · The preprocessor, multi-file programs and make · topic 36 of 60 · 25 min
The preprocessor and macros
Explanation
What the preprocessor does
The preprocessor is the first stage of compilation (module 1). It knows nothing about C types or syntax; it works on text. It removes comments, pastes in the contents of #include files, replaces macros, and keeps or drops code based on #if conditions. You can see its output with gcc -E file.c.
Object-like macros
#define GST_RATE 18
#define MAX_STUDENTS 60
#define APP_NAME "Fee Manager"
int marks[MAX_STUDENTS]; /* becomes: int marks[60]; */
printf("%s v1\n", APP_NAME);By convention macro names are UPPER_CASE so readers know they are not variables. A macro has no type and no scope: it is replaced everywhere after its definition. A const variable (const double GST_RATE = 18.0;) or an enum constant is often better because it has a type and shows up in the debugger; macros remain common for array sizes in C and for conditional compilation.
Function-like macros and their traps
#define SQUARE_BAD(x) x * x
SQUARE_BAD(1 + 2) /* becomes 1 + 2 * 1 + 2 = 5, not 9 */
10 / SQUARE_BAD(5) /* becomes 10 / 5 * 5 = 10, not 0 */
#define SQUARE(x) ((x) * (x)) /* bracket every parameter AND the whole body */
SQUARE(1 + 2) /* ((1 + 2) * (1 + 2)) = 9 */
int i = 3;
SQUARE(i++) /* ((i++) * (i++)): i incremented twice → undefined behaviour */
#define MAX(a, b) ((a) > (b) ? (a) : (b)) /* the larger argument is evaluated twice */Multi-statement macros
#define LOG_ERROR(msg) do { fprintf(stderr, "[%s:%d] %s\n", __FILE__, __LINE__, msg); } while (0)
if (failed) LOG_ERROR("could not open config"); /* works safely even without braces */
else puts("ok");# and ##
#define STR(x) #x /* "stringify": STR(price) → "price" */
#define FIELD(name) int field_##name /* token pasting: FIELD(qty) → int field_qty */Predefined macros
printf("%s:%d in %s, built %s %s\n", __FILE__, __LINE__, __func__, __DATE__, __TIME__);
/* __func__ (C99) is the current function's name; __STDC_VERSION__ tells you the C standard in use */Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
The preprocessor and macros · 1/6
In this lesson you will learn what the preprocessor does with lines that start with a hash.
Prefer a video by a teacher? Find free videos on “c preprocessor macros define pitfalls inline functions” on YouTube ↗
In real life: A logging macro with file and line
Embedded teams often use a logging macro that automatically records the file and line, so field reports from devices point to the exact source line without the developer typing it. A static inline helper would not know the caller's __LINE__, which is one place where macros are genuinely needed.
#define LOG(level, fmt, ...) \
fprintf(stderr, "%s %s:%d: " fmt "\n", level, __FILE__, __LINE__, __VA_ARGS__)
LOG("WARN", "battery at %d%%", 12); /* WARN meter.c:88: battery at 12% */Practice (0/3 done)
#define DOUBLE(x) x + x What is 3 * DOUBLE(4)?
#define MAX(a, b) ((a) > (b) ? (a) : (b)) int i = 5, j = 3; int m = MAX(i++, j); What are m and i?
Write a header with: an object-like macro for a buffer size, a correctly bracketed MIN macro, the same logic as a static inline function, and a do-while(0) LOG macro using __FILE__ and __LINE__. Show with gcc -E what MIN(a + 1, b * 2) expands to, and demonstrate the double-evaluation problem with MIN(x++, y).
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Conditional compilation and include guardsThe preprocessor, multi-file programs and make · same module
- Splitting a program into filesThe preprocessor, multi-file programs and make · same module
- Building with makeThe preprocessor, multi-file programs and make · same module
- Unit testsRust Programming · Testing, tooling and unsafe Rust
- Functions: declarations, expressions and returnJavaScript Programming (language in depth) · Functions and closures
- C interview questionsEmbedded C, system calls and careers
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
When does macro replacement happen?
Answer: Before compilation, in the preprocessor
It is a text step.
Why wrap multi-statement macros in do { … } while (0)?
Answer: So the macro acts as one statement, even in if/else without braces
Avoids broken if/else.
What does #x do in a macro body?
Answer: Turns the argument into a string literal
Stringification.
Which is usually better than a function-like macro for simple calculations?
Answer: A static inline function
Type-checked and evaluates arguments once.
Which macro is predefined when compiling for Windows?Related
Answer: _WIN32
_WIN32 is defined for 32- and 64-bit Windows targets.
Can unit tests call private functions of their module?Related
Answer: Yes, because the tests module is a child module
Child modules can see private items of their parent.
From Unit tests
What does a function return if it has no return statement?Related
Answer: undefined
Every JavaScript function without a return value gives undefined.
Which include style is used for your own header file?Related
Answer: #include "myfile.h"
Quotes search your project folder first; angle brackets are for standard and system headers.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 4 more topics in “The preprocessor, multi-file programs and make” before its exam: Conditional compilation and include guards, Splitting a program into files, Building with make, Libraries: static and shared.
- Module 8 exam: Preprocessor, multi-file programs and make · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Bits, undefined behaviour and safety. 60 topics left in the course before the final exam.