宏定义必须为参数和整体加括号,如#define SQUARE(x) ((x)*(x)),否则运算符优先级导致逻辑错误;宏体不加末尾分号,多行宏用do-while(0)包裹以支持语句式调用。

宏定义写错括号会直接导致运算逻辑翻车
宏不是函数,它只是文本替换。没加括号的 #define SQUARE(x) x * x 看似没问题,但 SQUARE(2 + 3) 展开后变成 2 + 3 * 2 + 3,结果是 11 而不是 25。
- 所有带参数的宏,参数名在替换体中必须用括号包住:
#define SQUARE(x) ((x) * (x)) - 整个宏体也建议加外层括号,避免和周边运算符结合出错,比如用在
if (a > SQUARE(b))里 - 宏体内不要写分号,调用处才由用户决定是否加分号;加了会导致
if (cond) MACRO(); else ...编译失败
用 do-while(0) 包裹多行宏才能安全当语句用
想让宏支持 if / for 后直接跟、还能加分号结束?裸写多行代码会崩。比如:
#define LOG(msg) \
std::cout << "[LOG] "; \
std::cout << msg << std::endl这段在 if (flag) LOG("hi"); else std::cout 中,<code>else 会找不到匹配的 if,因为宏展开后分号只结束第一行。
- 正确写法是:
#define LOG(msg) do { std::cout -
do-while(0)让宏语法上是一个完整语句,既支持分号结尾,又不会意外循环 - 别用
{}单独包——它不是语句,嵌套if-else时仍会出作用域问题
宏名全大写 + 下划线是硬性习惯,不是风格问题
不是为了“好看”,而是为了快速区分:这是预处理期就没了的东西,不占运行时符号表,调试器看不到,IDE 无法跳转,也不能被重载或模板推导。
立即学习“C++免费学习笔记(深入)”;
- 写成
#define max(a,b) ((a)>(b)?(a):(b))看似省事,但会覆盖标准库std::max,且类型不安全 - 真正该用宏的地方极少:条件编译(
#ifdef DEBUG)、平台判断(#ifdef _WIN32)、生成重复样板代码(如枚举+字符串映射) - 能用
constexpr、inline函数或模板代替的,优先不用宏——它们有类型检查、可调试、支持重载
宏里用 # 和 ## 拼接字符串或标识符要小心空格和求值时机
# 把参数转成字符串字面量,## 把两个 token 拼成一个新标识符。但它们只对宏参数生效,不展开宏本身。
-
#define STR(x) #x→STR(HELLO)得"HELLO",但#define A 123后STR(A)得"A",不是"123";要二次宏展开才取到值 - 拼接时如果参数本身是宏,需先展开再拼:
#define CONCAT(a, b) DO_CONCAT(a, b)+#define DO_CONCAT(a, b) a##b -
##两端不能是空 token,CONCAT(x, )会报错;末尾拼接常用于生成带编号的变量名,比如日志钩子
宏的边界很脆——它在编译最前端运行,不理解 C++ 语法,只认字符流。越想让它“智能”,越容易掉进展开顺序和 token 切分的坑里。











