带参数宏是文本替换而非函数调用,正确写法需为每个参数和整个表达式加括号,如#define square(x) ((x) (x));否则因运算符优先级导致错误,如square(1+2)展开为1+21+2=5而非9。

带参数宏的基本写法和括号陷阱
带参数宏不是函数,它只是文本替换,#define 后面跟宏名、括号、参数列表,然后是替换体。最常见错误是漏掉括号或括号位置不对,导致运算符优先级出问题。
- 错误写法:
#define SQUARE(x) x * x→SQUARE(1+2)展开为1+2 * 1+2,结果是5而非9 - 正确写法:
#define SQUARE(x) ((x) * (x))→ 每个参数都加括号,整个表达式也加括号 - 参数本身不能带空格或逗号;多个参数用逗号分隔,如
#define MAX(a, b) ((a) > (b) ? (a) : (b))
宏参数中的副作用问题(比如自增)
宏不做求值,只做拷贝粘贴,所以 x++ 这类有副作用的表达式在宏里会被展开多次,引发未定义行为。
-
#define SQUARE(x) ((x) * (x))遇到SQUARE(i++)会变成((i++) * (i++)),i自增两次,结果不可预测 - 避免方式:不用带副作用的实参;或改用
inline函数替代(C++ 推荐) - 若必须用宏,可借助 GNU 扩展的语句表达式(
({ ... })),但非标准 C++,移植性差
字符串化和连接操作符(# 和 ##)
# 把参数转成字符串字面量,## 把两个 token 拼接成一个新标识符——这是宏独有的能力,函数做不到。
-
#define STR(x) #x→STR(hello)展开为"hello";注意:#不展开参数,STR(PI)就是"PI",不是"3.14" -
#define CONCAT(a, b) a##b→CONCAT(f, oo)展开为foo;CONCAT(1, 2)是非法的(数字字面量不能拼接) - 组合用法:
#define DEBUG_PRINT(x) printf("%s = %d\n", #x, (x))→DEBUG_PRINT(count)输出count = 42
宏 vs inline 函数:什么时候该换掉宏
除非需要字符串化、token 拼接,或必须在预处理阶段介入(比如条件编译),否则优先用 inline 函数。
- 类型安全:宏不检查参数类型,
MAX(3.5, 4)可能隐式转换出错;inline函数支持重载和模板推导 - 调试友好:宏展开后调试器看不到原始调用点;
inline函数通常保留符号信息 - 参数只求值一次:
inline int max(int a, int b) { return a > b ? a : b; }安全处理i++
宏的灵活性是双刃剑,用得越少,代码越可控。真正绕不开它的场景其实不多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











