C Mastery / The Preprocessor: Function-Like, Variadic, Stringification, Pasting
Part 1 — The Core Language

The Preprocessor: Function-Like, Variadic, Stringification, Pasting

This chapter covers function-like macros, variadic macros, and the two special operators # (stringification) and ## (token pasting). These are powerful but error-prone; they must be used with precise parenthesization.

Why This Matters

Function-like macros can eliminate function-call overhead and generate code, but their naive use causes double-evaluation bugs and precedence errors. The # and ## operators are the basis of logging macros and code generation.

Prerequisites

Core Concept

Function-like macros

A function-like macro has a parameter list:

#define SQUARE(x) ((x) * (x))

Used as SQUARE(5)((5) * (5)). Note the double parentheses: one around each parameter, one around the whole expression. This is essential to avoid precedence and double-evaluation bugs.

Double evaluation

SQUARE(i++) expands to ((i++) * (i++)), which increments i twice and is undefined behavior. Function-like macros do not evaluate arguments once like functions do.

Variadic macros (C99)

#define LOG(fmt, ...) printf(fmt, __VA_ARGS__)

__VA_ARGS__ expands to the trailing arguments. The ##__VA_ARGS__ extension (GCC/Clang) removes the preceding comma when no variadic arguments are given, but it is non-standard.

Stringification (#)

#param turns the argument into a string literal:

#define STR(x) #x
STR(hello)   /* "hello" */

Token pasting (##)

a ## b concatenates tokens a and b into a single token:

#define CONCAT(a, b) a ## b
CONCAT(foo, bar)   /* foobar */

Syntax

#define NAME(params) replacement
#define NAME(...) replacement              /* variadic */
#define NAME(params, ...) replacement
#param
tok1 ## tok2

Examples

Safe function-like macro

#define MAX(a, b) ((a) > (b) ? (a) : (b))

Logging with __FILE__ and __LINE__

#include <stdio.h>

#define LOG_MSG(msg) \
    printf("%s:%d: %s\n", __FILE__, __LINE__, msg)

int main(void)
{
    LOG_MSG("something happened");
    return 0;
}

Stringification and token pasting

#include <stdio.h>

#define STR(x) #x
#define GLUE(a, b) a ## b

int main(void)
{
    int foobar = 42;
    printf("%s\n", STR(hello world));   /* "hello world" */
    printf("%d\n", GLUE(foo, bar));     /* foobar -> 42 */
    return 0;
}

Expected output:

hello world
42

How It Works

The preprocessor substitutes arguments into the replacement list, then applies # and ## (before ordinary macro expansion, in a specific order). The result is re-scanned for further macro expansion. This rescanning can produce surprising results, especially with ##.

Variations

Variadic macro comma handling

Standard C requires at least one variadic argument when ... is used. The ,##__VA_ARGS__ GNU extension allows zero. For portable code, provide a default or use a different design.

Recursive macros (not allowed)

Macros do not expand recursively. A macro that references itself during its own expansion is not expanded again, which prevents infinite recursion.

Common Mistakes

Undefined Behavior

object (e.g., SQUARE(i++)) is UB. VERIFIED

constraint violation.

Portability

Under the Hood

All macro expansion happens in the preprocessor, before the compiler sees the code. There is no run-time cost and no type checking. The compiler only sees the expanded tokens.

Practical Usage

function call is undesirable and side-effect arguments are avoided.

evaluation matter.

Exercises

1. Write a MAX macro and demonstrate a precedence bug, then fix it with parentheses. 2. Show the double-evaluation bug with SQUARE(i++) and explain the UB. 3. Write a logging macro using # and __LINE__. 4. Use ## to generate unique variable names in a macro.

Deep Challenge

Write a macro SWAP(a, b) that swaps two variables of the same type *without* double-evaluating their arguments. Explain why this is difficult or impossible with a pure function-like macro, and propose a static inline or _Generic alternative that is safe.

References

(##).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.pp.fn-macro06
c.pp.stringify05
c.pp.paste05
c.pp.variadic-macro05