C Language Macro Expansion Rules: The Magic of Macros in the Linux Kernel

1. Basics of Macro Definition: The Essence of Text Replacement

A macro is one of the core functionalities of the C language preprocessing phase, and it is essentially text replacement, expanded by the preprocessor before compilation.

Basic Syntax

#define macro_name replacement_text

Example:

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

Classic Macros in the Linux Kernel

#define likely(x)   __builtin_expect(!!(x), 1)  // Branch prediction optimization
#define unlikely(x) __builtin_expect(!!(x), 0)

2. Core Rules of Macro Expansion

Rule 1: Pure Text Replacement

A macro is merely string replacement, without type checking or computation:

#define SQUARE(x) x * x
int a = 3;
printf("%d\n", SQUARE(a + 1));  // After replacement: a + 1 * a + 1 → 3 + 1 * 3 + 1 = 7 (unexpected 16)

Correct Usage:

#define SQUARE(x) ((x) * (x))  // Adding parentheses to ensure order of operations

Rule 2: Delayed Expansion of Macro Parameters

Macro parameters are not evaluated immediately during replacement, but are replaced first and then computed:

#define DOUBLE(x) x + x
int a = 5;
printf("%d\n", DOUBLE(a++));  // After replacement: a++ + a++ → 5 + 6 = 11 (and a becomes 7)

⚠️ Side Effect Warning: Avoid using macro parameters with <span>++</span>, <span>--</span>, and other operations that may be evaluated multiple times.

3. Special Macro Techniques

(1) Stringification (#)

Using <span>#</span> to convert macro parameters to strings:

#define STR(s) #s
printf("%s\n", STR(hello));  // Outputs "hello"

(2) Concatenation (##)

Using <span>##</span> to concatenate identifiers:

#define CONCAT(a, b) a##b
int xy = 100;
printf("%d\n", CONCAT(x, y));  // Outputs the value of xy: 100

(3) Variadic Macros (__VA_ARGS__)

Macros that support variable arguments:

#define LOG(fmt, ...) printf("[LOG] " fmt, ##__VA_ARGS__)
LOG("value=%d\n", 42);  // Outputs: [LOG] value=42

4. Advanced Macro Applications in the Linux Kernel

(1) <span>container_of</span> Macro

#define container_of(ptr, type, member) ({              \
    const typeof(((type *)0)->member) *__mptr = (ptr);  \
    (type *)((char *)__mptr - offsetof(type, member)); \
})

Function: Finds the address of the entire structure from a pointer to a structure member (widely used in linked lists and device drivers).

(2) <span>BUILD_BUG_ON</span> Compile-time Assertion

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

Function: If the condition is true, a compile-time error occurs (array size becomes negative).

5. Macros vs Inline Functions

Feature Macro Inline Function
Processing Stage Preprocessing (text replacement) Compilation (generating machine code)
Type Safety ❌ No type checking ✅ Type checking present
Debugging ❌ Difficult to debug (code after replacement) ✅ Can be debugged normally
Use Case Simple replacements, code generation Complex logic, performance optimization

Linux Kernel Preference:✅ Use macros for high-frequency small operations (like <span>likely/unlikely</span>)✅ Use <span>static inline</span> functions for complex logic

6. Pitfalls and Best Practices of Macros

Common Pitfalls

  1. Operator Precedence Issues (missing parentheses leading to errors)
  2. Multiple Evaluations of Parameters (e.g., <span>MAX(a++, b++)</span>)
  3. Macro Name Conflicts (avoid using names starting with <span>_</span>, which may conflict with system macros)

Best Practices

  1. Macro Names in Uppercase (e.g., <span>MAX_LEN</span>)
  2. Add Parentheses to Parameters and Overall (e.g., <span>#define MUL(a, b) ((a) * (b))</span>)
  3. Use <span>inline</span> functions for complex logic

Conclusion

  1. Macros are text replacements in the preprocessing phase, flexible but prone to errors.
  2. <span>#</span> and <span>##</span> achieve stringification and concatenation.
  3. The Linux kernel extensively uses macros for optimization (like <span>container_of</span><span>).</span>
  4. Prefer inline functions, using macros only for simple replacements or special cases.

Question: What is the output of the following macro?

#define M(x, y) x##y
int ab = 100;
printf("%d", M(a, b));

(Answer: Outputs the value of <span>ab</span>, which is <span>100</span>)

Leave a Comment