宏是纯文本替换,无类型检查、不占内存、不支持作用域;易因括号缺失、运算符优先级、副作用等引发错误;仅字符串化、标识符拼接和条件编译不可替代。

宏定义不是变量,它只是文本替换
写 #define PI 3.14159 后,PI * r * r 在预处理阶段直接变成 3.14159 * r * r——中间不经过类型检查、不占内存、不支持作用域。很多人误以为它像 const double PI = 3.14159; 一样安全,其实完全不是。
- 宏没有类型,
#define MAX(a,b) a > b ? a : b遇到MAX(x++, y++)会把自增执行两次 - 宏展开不考虑运算符优先级:
#define SQUARE(x) x * x用在SQUARE(2 + 3)上结果是2 + 3 * 2 + 3 == 11,不是 25 - 调试器看不到宏,断点打不进去,
gdb里查不到PI的值
带参宏必须加括号,连参数和整个表达式都要包
这是最常漏掉的一环。只给外层加括号没用,参数本身也得包,否则一遇上 +、&&、++ 就翻车。
#define SQUARE(x) ((x) * (x)) // ✅ 正确 #define MAX(a, b) ((a) > (b) ? (a) : (b)) // ✅ 正确 #define BAD_SQUARE(x) (x * x) // ❌ 错误:SQUARE(1+2) → 1+2*1+2
- 所有参数出现的位置都套
(x),不能只信“我传进去的是变量” - 整个宏体再套一层
(...),防止用在if或赋值右侧时被截断 - 宏末尾**不能加分号**:
#define FOO() do { ... } while(0);末尾的分号会和调用处的分号连成两个,导致语法错误
用 do { ... } while(0) 写多行宏才安全
想在宏里写 if、for 或多条语句?直接用花括号会破坏 if-else 逻辑。比如:
#define LOG(msg) { printf("LOG: "); printf(msg); }\nif (debug) LOG("start"); else printf("skip");这实际展开为:
立即学习“C++免费学习笔记(深入)”;
if (debug) { printf("LOG: "); printf("start"); }; else printf("skip");语法错误——因为宏末尾那个分号把 else 拆开了。
- 正确写法是
#define LOG(msg) do { printf("LOG: "); printf(msg); } while(0) -
do-while(0)让宏行为像单条语句,能跟在if后面、后面加分号也不冲突 - 注意:里面定义的变量会泄漏到外层作用域(C++17 起可用
[[maybe_unused]]压制警告,但最好避免在宏里声明变量)
宏 vs constexpr / inline 函数,什么情况非用宏不可
绝大多数场景下,constexpr 变量、inline 函数、模板比宏更安全、可调试、支持重载。宏只剩三个真实不可替代的用途:
- 字符串化:
#define STR(x) #x→STR(hello)展开为"hello",constexpr做不到 - 拼接标识符:
#define CONCAT(a, b) a##b→CONCAT(foo, _bar)得到foo_bar - 条件编译:
#ifdef DEBUG这类控制代码是否参与编译,函数做不到 - 其他情况——比如想“绕过类型系统”或“生成一堆重复样板”,先想想能不能用模板或概念代替
宏一旦变复杂,就该警惕:是不是在重复造轮子,或者掩盖了设计缺陷?它不像函数那样有调用栈、不参与 ADL、不支持 SFINAE,出问题时排查路径比普通 C++ 代码长得多。










