C / PREPROCESSOR
Object-like macros and their parenthesis traps
Write object-like macros that survive substitution: parenthesize multi-token expression bodies, and know the cases where parentheses break the code.
What you will learn
- Expand a macro by hand at the use site, then apply precedence, to predict its value
- Wrap multi-token expression bodies in one outer pair of parentheses
- Leave type names, brace initializers and string literals unparenthesized
- Check what the compiler really sees with cc -E instead of guessing
Understanding Object-like macros and their parenthesis traps
#define KB 1024 binds the identifier KB to a replacement list: the sequence of preprocessing tokens after the name, up to the end of the line. Every later occurrence of KB as a whole token is removed and that list is inserted in its place, and that is the entire operation — the preprocessor performs no arithmetic and knows nothing about C's grammar. So #define AREA 2 + 3 does not define the value 5; it defines the three tokens 2, +, 3, and those tokens are only parsed after they have landed inside some larger expression.
That is where the trap lives. Because grouping is decided after substitution, the operators next to the use site compete with the operators inside the body, and the tighter-binding one wins: AREA * 10 becomes 2 + 3 * 10, which is 32, and a bare 1 << 3 in FLAG + 1 becomes 1 << 3 + 1, that is 1 << 4, because + binds tighter than <<. Wrapping the whole replacement list in parentheses turns it into a primary expression, a unit nothing outside can reach into, and that is the only reason the convention exists. Substitution works on tokens rather than characters, so you never accidentally glue a - and a -1 into --; re-association is the real hazard, and parentheses close it.
Parentheses are a rule about expressions, not about macros. A body like "core" must stay bare, because adjacent string literals are joined by a grammar rule about literal tokens, and a parenthesized literal is an expression that cannot be concatenated; the same applies to a body that is a type name, a brace initializer such as { 0, 0 }, or any fragment of syntax rather than a value. Never end a replacement list with a semicolon either: the definition itself is never checked, so #define MAX 100; is accepted quietly and then fails at if (n < MAX), with the error pointing at the use site. When in doubt, run cc -E on the file and read the token stream the compiler will actually parse.
<stdio.h>
SUM_BARE
SUM_SAFE
BIT_BARE
BIT_SAFE
int main(void)
{
/* The tokens are pasted in first; C precedence is applied afterwards. */
printf("2 + 3 * 10 = %d\n", SUM_BARE * 10);
printf("(2 + 3) * 10 = %d\n", SUM_SAFE * 10);
printf("1 << 3 + 1 = %d\n", BIT_BARE + 1);
printf("(1 << 3) + 1 = %d\n", BIT_SAFE + 1);
return 0;
}
A macro is a token list spliced into your code, and precedence is applied only after the splice, so the outer parentheses are what stop neighbouring operators from tearing the body apart.
Worked examples
A bit mask that is always non-zero
An unparenthesized bitwise body loses its left half to the & of the use site, so the mask test never fails.
<stdio.h>
LOWBITS_BARE
LOWBITS_SAFE
int main(void)
{
unsigned int reg = 0x80;
printf("bare: 0x%02X\n", reg & LOWBITS_BARE);
printf("safe: 0x%02X\n", reg & LOWBITS_SAFE);
return 0;
}
Example explained
Line 1& binds tighter than |, so the bare expansion parses as (reg & 0x0F) | 0x30, not reg & (0x0F | 0x30).
Line 2That leaves 0x30 as an unconditional part of the result, so the expression is non-zero for every value of reg and any if built on it is always taken.
Line 3The parenthesized version builds the mask 0x3F first; 0x80 has none of those bits, so the test correctly yields 0.
Bodies that must stay bare
Three macros in one file show that only multi-token expressions want the outer parentheses.
<stdio.h>
BUILD_TAG/* literal: must sit next to its neighbour */
SPAN/* expression: parenthesized */
ROWS/* one token: nothing to split */
int main(void)
{
char label[] = BUILD_TAG "-v2";
int grid[ROWS] = { SPAN, SPAN * 2, SPAN - 2 };
printf("%s %d %d %d\n", label, grid[0], grid[1], grid[2]);
return 0;
}
Example explained
Line 1BUILD_TAG stays bare because concatenation joins adjacent string literal tokens; a parenthesized literal is an expression and cannot be concatenated, so the line would not compile.
Line 2SPAN * 2 expands to (7 - 2) * 2 and gives 10; bare it would expand to 7 - 2 * 2 and give 3.
Line 3ROWS is a single token, so no neighbouring operator can get inside it: parentheses are legal in the array size but buy nothing.
Important notes
Parentheses fix grouping, not the body itself: #define HALF (1 / 2) is 0 at every use site, because both operands are ints, and no amount of bracketing changes that.
Diagnostics point at the line that used the macro, not at the #define, and often quote tokens you never typed there; an error mentioning a value from a header is a strong hint that a macro body is at fault.
Common mistakes
Defining #define AREA 2 + 3 and then writing AREA * 10: the program prints 32, nothing is diagnosed, and the wrong figure propagates into every later calculation.
Parenthesizing a body that is not an expression, such as #define VER ("1.2"): VER " beta" now fails to compile, and the error is reported at the use site far from the #define.
Bracketing only part of the body, as in #define SHIFT (1) << 4: the << is still exposed, so SHIFT + 1 becomes (1) << 4 + 1, which is 32 rather than 17.
Try it yourself
Change, predict, then run
Define #define GAP 10 - 3 and print -GAP, GAP * 2 and 100 / GAP, writing down what you expect if GAP means 7. Then compare with the real output, and fix all three with a single pair of parentheses in the definition.
Open the C workspaceCheck your understanding
A header contains #define STEP 4 - 1, and the author intends STEP to mean the value 3. Which use site still produces the intended result?
- STEP + 1
- STEP * 2
- 12 / STEP
- -STEP
Show answer
STEP + 1 expands to 4 - 1 + 1; + and - have equal precedence and associate left to right, so the grouping is unchanged and the result is 4, exactly what (4 - 1) + 1 gives. STEP * 2 is the tempting choice because doubling looks harmless, but * binds tighter than -, so it parses as 4 - (1 * 2) = 2 instead of 6; likewise 12 / STEP is 12 / 4 - 1 = 2 and -STEP is -4 - 1 = -5. The one correct case is an accident that holds only for that particular neighbour, which is why the parentheses belong in the definition.