Macros and File Inclusion
#define for object-like and function-like macros, the parenthesization traps, #include, and macros versus functions.
The Pass Before the Compiler
Chapter 1 described the four build stages, and the first of them, the preprocessor, has been working for you ever since: every #include and every #define PI 3.14159 in the course so far went through it. What that lesson said in one sentence, this chapter takes seriously: the preprocessor pastes text. It never evaluates an expression, never checks a type, never sees a variable; it edits your source before the compiler proper reads a single declaration. Every macro trap in this lesson, and there are three of them, follows from that one fact, and every fix is a way of pasting text that stays correct no matter what surrounds it.
Object-Like Macros, Revisited
The form the course has used since chapter 2:
#define PI 3.14159
#define MAX_VALUES 100
A #define names a piece of replacement text: from that line to the end of the file, every place the name appears as its own token, the preprocessor substitutes the text. There is no equals sign and no semicolon, because nothing is being assigned; the directive is not a statement, and everything after the name is replacement text taken literally. Write #define MAX_VALUES 100; and the semicolon becomes part of the text, so int values[MAX_VALUES]; pastes into int values[100;];, a syntax error two lines away from its cause. The UPPER_CASE convention exists exactly because substitution is invisible at the use site: shouting names warn the reader that MAX_VALUES is not a variable, has no address, and obeys none of the scope rules that variables do.
Function-Like Macros
Put a parenthesized parameter list immediately after the name, and the macro takes arguments:
area of circle, r = 2.0: 12.566360
SQUARE(n) = 49
SQUARE(2 + 3) = 25
SQUARE(radius) becomes ((radius) * (radius)) before the compiler sees it; SQUARE(2 + 3) becomes ((2 + 3) * (2 + 3)), which is 25. The same macro squared a double and two int expressions, something no single C89 function can do, and that flexibility is exactly what a function-like macro buys. Notice the shape of the definition: every use of the parameter is parenthesized, and the whole body is wrapped in one more pair. That is not decoration. It is the entire safety mechanism, and the next section shows what each layer of parentheses is defending against.
The Two Parenthesization Traps
The following two definitions are wrong, and they are wrong in the two distinct ways an unparenthesized macro fails; the program prints their wrong answers next to the correct ones:
BAD_SQUARE(2 + 3) = 11
SQUARE(2 + 3) = 25
100 / HALF_SQUARE(5) = 100
100 / SQUARE(5) = 4
Trap one is the unparenthesized parameter. BAD_SQUARE(2 + 3) pastes to 2 + 3 * 2 + 3, and the compiler applies chapter 3's precedence rules to the pasted text: the multiplication binds first, giving 2 + 6 + 3, which is 11, not 25. The argument was never "the value 5"; it was the three tokens 2 + 3, dropped into the body verbatim.
Trap two is the unparenthesized body. HALF_SQUARE parenthesizes its parameter, so it survives trap one, but 100 / HALF_SQUARE(5) pastes to 100 / (5) * (5). Division and multiplication share a precedence level and associate left to right: 100 / 5 is 20, times 5 is 100, five times too large. The outer parentheses in the correct SQUARE make the pasted text 100 / ((5) * (5)), an indivisible operand, which is 4. Hence the rule the course applies to every macro from here on: parenthesize every parameter and the whole body, always, even when today's uses would survive without it, because the macro cannot know what operators tomorrow's caller will put next to it.
The Argument Is Pasted, Not Passed
A function evaluates its argument once and works with the value. A macro pastes the argument's text into every place the parameter appears, and SQUARE uses its parameter twice. So this call:
r = SQUARE(i++); /* WRONG: expands to ((i++) * (i++)) */
modifies i twice with no sequence point between the two modifications, which is chapter 3's unsequenced-modification rule: undefined behaviour, the same crime as i * i++, merely hidden behind a tidy-looking name. The standard promises nothing about r or about i afterwards. GCC can see through this one, from a real compile of that line:
warning: operation on 'i' may be undefined [-Wsequence-point]
but it cannot catch every disguise, so the working rule is absolute: never pass an expression with a side effect, i++, --n, an assignment, a function call that changes state, as a macro argument. Evaluate it into a plain variable first and pass the variable. The double evaluation also costs time when the argument is expensive: SQUARE(computeLoad()) calls computeLoad twice, which a function version would not.
Macros versus Functions
Chapter 6 gave you functions; this lesson gives you a second tool that looks similar at the call site, so the differences need stating plainly. A macro has no type checking: paste nonsense in and the compiler diagnoses the expansion, in whatever mangled form the paste produced. A macro has no address: MAX2 names replacement text, not code, so it cannot be assigned to chapter 12's function pointers or passed to qsort as a comparator. And a macro is expanded at every use site, growing the program instead of jumping to one shared body. What it buys in exchange is the type flexibility shown here:
MAX2(3, 9) = 9
MAX2(2.5, 1.5) = 2.5
maxInt(3, 9) = 9
One MAX2 serves int and double; maxInt serves exactly int, but it type-checks its arguments, evaluates them once each, has an address, and can be stepped into in a debugger. The honest modern verdict: write logic as functions; reserve macros for named constants and for the conditional compilation of the next lesson. In C89 a small macro like MAX2 also avoids function-call overhead, which mattered on the machines this dialect grew up on and is the reason old codebases are full of them, but correctness, not speed, should decide, and functions are the correct default.
#include: Quotes Versus Angle Brackets
#include is the other paste operation: the directive is replaced by the entire text of the named file, which is why chapter 1 could call stdio.h "pasted in". The two spellings differ only in where the preprocessor searches. #include <stdio.h> looks in the implementation's system directories, where the standard headers live. #include "myheader.h" looks first in the directory of the file doing the including, and only if that fails falls back to the same system search. The convention that follows, and that every C codebase you will read obeys: angle brackets for system headers, quotes for your own project's headers. When chapter 15 splits a program across files, each .c file will #include "its-module.h" and prove the header self-contained by including it first. What happens when the same header gets pasted twice into one file, and the #ifndef guard that prevents it, is the next lesson's job.
The Space Before the Parenthesis
One character separates the two macro kinds, and it is a space. This definition looks function-like and is not:
#define F (x)
A macro is function-like only when the ( touches the macro name. Here there is a space first, so F is an object-like macro whose replacement text is the three tokens (x). With an int x = 5; in scope, printf("%d\n", F); pastes to printf("%d\n", (x)); and prints 5, no error anywhere, just a program quietly using a variable you never meant to name. Write F(2) instead and it pastes to (x)(2), an attempt to call x as a function, and the compiler's complaint points at the use site, not at the stray space that caused it. When a function-like macro is intended, the parenthesis goes hard against the name: #define F(x) ....
Key Takeaways
- The preprocessor pastes text before compilation; it never evaluates, so every macro question is answered by writing out the expansion.
- Object-like macros take no equals sign and no semicolon; a trailing semicolon becomes part of the replacement text and breaks the use sites, not the definition.
- Parenthesize every macro parameter and the whole body:
#define SQUARE(x) ((x) * (x)). Unparenthesized,SQUARE(2 + 3)pastes to2 + 3 * 2 + 3= 11, and a body without outer parentheses turns100 / SQUARE(5)into100 / (5) * (5)= 100. - A macro pastes its argument into every use of the parameter, so
SQUARE(i++)modifiesitwice with no sequence point: undefined behaviour. Never pass side effects to macros. - Macros have no type checking, no address, and expand at every use; functions type-check, evaluate arguments once, and can be pointed at. Functions for logic; macros for constants and conditional compilation.
#include <...>searches the system directories;#include "..."searches the including file's directory first, then the system path. Angle brackets for standard headers, quotes for your own.#define F (x)is an object-like macro with body(x): only a(immediately after the name, no space, makes a macro function-like.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Macros and File Inclusion - Quiz
Test your understanding of the lesson.
Practice Exercises
Temperature Macros
Define two fully parenthesized function-like macros above main: CELSIUS_TO_F(c), which converts Celsius to Fahrenheit as ((c) * 9.0 / 5.0) + 32.0 with the whole body in one more pair of parentheses, and MAX2(a, b), which yields the larger of its two arguments with ?:. Read two Celsius temperatures with one guarded scanf ("%lf %lf"); on bad input print "invalid input" and return 1. Convert each temperature exactly once into a variable, then print both Fahrenheit values and the warmer of the two, each to one decimal place. Passing the converted variables, not nested macro calls with side effects, to MAX2 is the point of the exercise.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!