Expansion Mechanics
Argument expansion order, the double-expansion idiom, stringize and paste โ what the preprocessor actually does with your text.
Arguments expand before substitution โ except with # and
SQUARE(x) ((x) * (x)) replaces x with the already expanded argument โ unless x is used with # (stringize) or ## (paste), where it stays raw. This subtlety is why the classic double-expansion wrapper exists:
#define STR_(x) #x /* x stays unexpanded: becomes "x" */
#define STR(x) STR_(x) /* x expands first, then stringizes */
STR(__LINE__) gives "14"; STR_(__LINE__) gives "LINE".
Parenthesize everything
#define BAD(a, b) a + b and BAD(1, 2) * 3 expands to 1 + 2 * 3. Every parameter reference gets parentheses; the whole replacement gets parentheses. Macro code that violates this is not stylistically wrong โ it computes wrong answers.
Multi-line and do-while
Statement-shaped macros wrap in do { ... } while (0) so if (x) MACRO(); else ... still parses โ a plain { } would break the semicolon. This is not superstition; it is syntax maintenance.