C 语言预处理进阶
更新时间:2026-08-26。本文是
languages/c/主题高手层的骨架文档(占位),完整展开将在后续批次补齐。
本文要回答的问题
- 函数式宏有哪些"看着对其实错"的坑?
##和#在宏里是干嘛的?- X-Macro 是什么?为什么能"一次定义多处生成"?
一、函数式宏的陷阱
| 陷阱 | 反例 | 正解 |
|---|---|---|
| 参数缺括号 | #define SQ(x) x*x → SQ(a+b) 展开错 | #define SQ(x) ((x)*(x)) |
| 副作用重复 | MAX(a++, b) 可能自增两次 | 宏里别放带副作用的实参 |
| 悬空 else | if (x) DO(x); else ... | 宏体用 do { } while(0) 包裹 |
| 结尾分号 | 调用处多写 ; 出空语句 | 统一约定 |
二、# 与 ## 运算符
#x:把实参字符串化——#define STR(x) #x,STR(abc)得"abc"。a ## b:记号拼接——#define MK(name) x_ ## name,MK(foo)得x_foo。
拼接常用于"生成一组相似符号",配合枚举/数组做 X-Macro。
三、X-Macro 模式
先定义"数据表",再多次展开:
c
#define COLORS(X) \
X(RED, 0) X(GREEN, 1) X(BLUE, 2)
#define AS_ENUM(name, val) name = val,
enum Color { COLORS(AS_ENUM) };
#define AS_STR(name, val) #name,
const char *color_names[] = { COLORS(AS_STR) };一次修改 COLORS 表,枚举、名称、表项同步生成——单一数据源,避免"枚举改了数组忘了改"。
四、宏 vs 函数 vs 内联
| 维度 | 宏 | 函数 | inline 函数 |
|---|---|---|---|
| 类型检查 | 无 | 有 | 有 |
| 调用开销 | 无 | 有 | 无(编译期展开) |
| 调试 | 难(展开后) | 易 | 易 |
| 适用 | 常量、简单表达式、代码生成 | 通用逻辑 | 小函数热路径 |
inline 函数集两者之长:类型安全 + 无调用开销,现代代码优先用它替代小宏。
五、与入门层的衔接
一句话总结
宏是"编译前文本手术":函数式宏要防括号/副作用/悬空 else,#/## 和 X-Macro 能做代码生成,但能用 inline 函数就别硬上宏——文本替换越少,调试越轻松。
本文为骨架文档:核心结构已就位,示例代码与实测数据将在后续批次补齐。