Every macro/inline problem you will meet is one of these cells. Each worked example below is tagged with the cell it covers, and together they cover the whole grid.
Substitute the literal text 2 + 3 for x.
Why this step? The preprocessor copies characters, so SQ(2 + 3) becomes (2 + 3 * 2 + 3).
Now hand the result to the compiler, which applies real Operator Precedence in C: * before +.
Why this step? Precedence is a compiler concern, applied after substitution. So 2 + 3*2 + 3 = 2 + 6 + 3.
Add left to right: 2+6+3=11.
Why this step? All operators are now +, associativity is left-to-right, order doesn't change the sum.
Verify:r=11, not 25. Sanity: the outer parens around the whole body did not save us — the danger was between the two x uses. The real fix is #define SQ(x) ((x)*(x)), giving ((2+3)*(2+3)) = 25.
Substitute 4 for x: the line becomes 10 - 4 / 2.
Why this step? Blind paste — HALF(4) disappears, 4 / 2 appears in its place, unbracketed.
Apply precedence: / binds tighter than -, so it's 10 - (4/2).
Why this step? Here we got lucky — / outranks -, so this particular case happens to be safe.
Now change the surrounding operator: int r = HALF(4) * 3; becomes 4 / 2 * 3 = (4/2)*3 = 6, but you wanted(4/2)⋅3... which is also 6. Try int r = 12 / HALF(4); → 12 / 4 / 2 = (12/4)/2 = 1, but you wanted12/(4/2)=6. Now it breaks.Why this step? An un-parenthesized body is a landmine that only detonates for some surrounding operators. That unpredictability is exactly why the rule is "parenthesize the whole body too": #define HALF(x) ((x)/2).
Verify: with the safe macro, 12 / ((4)/2) = 12 / 2 = 6. ✅ The moral: wrap each parameter AND the entire body.
Substitute the text i++ for x: r = ((i++)*(i++));.
Why this step?x appears twice in the body, so the side-effecting text i++ is pasted twice.
Count the side effects: two i++ operations means i is incremented twice. But when each is read, and in what order, is Undefined Behavior in C — the C standard gives no guaranteed value.
Why this step? Parentheses only fix grouping; they cannot un-duplicate a side effect. On a typical compiler you might see r = 3*4 = 12 or 3*3 = 9 — but you must never rely on it.
Now the inline version: static inline int sq(int x){ return x*x; } then sq(i++).
Why this step? The argument i++ is evaluated exactly once to produce the parameter value 3, and theni becomes 4. Inside the body x*x = 3*3 = 9, deterministically.
Verify: inline gives r = 9, i = 4, guaranteed. The macro gives undefined results (double increment). This is the single strongest argument for preferring inline — see Function Call Overhead (which inline also removes when the compiler expands it).
POS(0) → ((0) < 0 ? 0 : (0)). Since 0 < 0 is false, the ternary yields 0.
Why this step? The boundary value 0 is not less than 0, so it survives unchanged — correct.
POS(-5) → ((-5) < 0 ? 0 : (-5)). -5 < 0 is true → yields 0.
Why this step? Negative input is clamped, as intended.
POS(-3 + 3) → (((-3 + 3)) < 0 ? 0 : ((-3 + 3))). Inner value is 0, 0 < 0 false → yields 0.
Why this step? Because each x is parenthesized, the sum -3 + 3 is evaluated as a group before comparison. Without those inner parens, -3 + 3 < 0 would still work here, but POS(a & b) type expressions would misbind — the degenerate arithmetic argument confirms our parens are correct.
Verify:POS(0)=0, POS(-5)=0, POS(-3+3)=0. All ≥0. ✅ Degenerate/boundary inputs pass because the ternary uses < (strict), so 0 maps to itself.
CUBE(2.5) → ((2.5)*(2.5)*(2.5)). Because 2.5 is a double literal, the compiler does floating multiplication.
Why this step? The macro has no type of its own — it inherits whatever type the argument's text carries. So 2.53=15.625.
icube(2.5): the parameter is int x, so 2.5 is converted to int, truncating to 2before the body runs.
Why this step? An inline function is fully type-checked. Passing 2.5 to int x narrows it to 2, then 23=8.
Compare: macro = 15.625, inline = 8.
Why this step? This is the double-edged sword. The macro is generic (works for double), the inline is type-safe (loudly commits to int). Neither is "wrong" — you choose based on need. See const and constexpr for typed constant alternatives.
Verify:2.53=15.625 (macro, kept as double); int truncation of 2.5 is 2, 23=8 (inline). ✅
Substitute: the if body becomes x = 0; y = 0;. So the full text is:
if (flag) x = 0; y = 0; // <-- two statements!else z = 1;
Why this step? An if without braces takes only the next single statement — that's x = 0;. The y = 0; is now a separate statement that always runs, and it sits between the if and the else.
The compiler sees else with no matching if → syntax error (error: 'else' without a previous 'if').
Why this step? Pasting two statements where one was expected shattered the if/else pairing.
Now RESET(x,y); expands to do{ (x)=0; (y)=0; } while(0); — one syntactic statement, and the trailing ; is consumed cleanly.
Why this step?do{...}while(0) bundles many statements into exactly one statement that also swallows a following semicolon, so it slots into if/else like a normal call would.
Verify: with the idiom, the if(flag) RESET(x,y); else z=1; parses correctly: flag true runs both assignments, false runs z=1. ✅
Substitute: ((next()) > (threshold) ? (next()) : (threshold)).
Why this step?a = next() appears twice. So next() is called once in the comparison and, if it wins, again when "returned".
Case split. If next() <= threshold: next() runs once (comparison), and the : branch returns threshold. If next() > threshold: next() runs twice (comparison and the ? branch) — and each call advances the stream, so it returns a different, later value than the one that won the comparison.
Why this step? The bug is intermittent because it only bites when next() > threshold. That's why it "sometimes works." Classic double-evaluation, exactly Cell C but hidden inside a real call.
Fix — inline function, which evaluates each argument once:
static inline int imax(int a, int b){ return a > b ? a : b; }int v = next();total += imax(v, threshold); // next() called exactly once
Why this step? The inline version computes next()once into a, guaranteeing the compared value and the returned value are the same. With -O2 it inlines to the same fast code — zero call overhead and correct.
Verify (numeric): suppose the stream yields 10, 99 on successive calls and threshold = 5. Macro: comparison call reads 10 (10 > 5 true), return call reads 99 → contributes 99 (wrong, and eats an extra stream value). Inline: reads 10 once, 10 > 5 → contributes 10 (correct). ✅
SQ(a - b) → substitute text a - b for each x: a - b * a - b.
Why this step? Blind paste, no inner parens → 4 - 2*4 - 2.
Apply precedence (* before -): 4 - (2*4) - 2 = 4 - 8 - 2 = -6.
Why this step? The middle * steals b and a, wrecking the subtraction grouping.
Now SQ(a - b) * 2 → a - b * a - b * 2 = 4 - 2*4 - 2*2 = 4 - 8 - 4 = -8.
Why this step? The trailing * 2 binds only to the last pasted b, not the whole result — a fourth surprise. The intended (a−b)2⋅2=4⋅2=8 is nowhere close.
Correct macro #define SQ(x) ((x)*(x)): SQ(a-b) → ((4-2)*(4-2)) = 4; SQ(a-b)*2 → ((4-2)*(4-2))*2 = 8.
Why this step? Full parenthesization (each param + whole body) makes both cases match the intended algebra.
Verify: buggy: SQ(a-b) = -6, SQ(a-b)*2 = -8. Fixed: 4 and 8. ✅ Every trap traced to text substitution + precedence.
Q1 ::: The danger is between the two xs (2+3 * 2+3); only per-parameter parens ((x)*(x)) fix it, not just wrapping the body.
Q2 ::: Becomes 12/4/2 = 1 (left-to-right) instead of 12/(4/2)=6; fix with ((x)/2).
Q3 ::: Macro pastes i++ twice → two side effects, order unspecified → UB; inline evaluates the argument once before the body.
Q4 ::: Returns 0; 0 < 0 is false so 0 maps to itself — strict < keeps the boundary correct.
Q5 ::: Macro is typeless text → 2.5 stays double → 15.625; inline int x truncates 2.5→2 → 8.
Q6 ::: do { ... } while(0).