<p>加括号很重要,因为预处理器仅做文本替换而不考虑运算优先级,不加括号会导致如10 / TWO_PI展开为10 / 2 * PI而结果错误;所有带运算的宏值及参数宏都必须用小括号包裹。</p>

用 #define 定义宏常量时,为什么加括号很重要
不加括号的宏在参与运算时容易出错,因为预处理器只是做纯文本替换,不考虑运算优先级。#define PI 3.14159 看似安全,但 #define TWO_PI 2 * PI 展开后是 2 * 3.14159,而 #define TWO_PI 2 * PI 如果后续被写成 TWO_PI + 1,实际变成 2 * PI + 1(正确),但若写成 10 / TWO_PI,就等价于 10 / 2 * PI,结果翻倍——这不是你想要的。
- 所有带运算的宏定义必须用小括号包裹整个值:
#define TWO_PI (2.0 * PI) - 连带参数宏也一样:
#define SQUARE(x) ((x) * (x)),否则SQUARE(a + b)展开为a + b * a + b - 字符串化、连接等高级用法暂不推荐新手碰,先守住括号底线
#define 和 const / constexpr 该选哪个
现代 C++ 中,#define 不再是定义常量的首选。它没有类型、不进调试符号、无法作用域控制,且可能污染全局命名空间。
- 普通整型/浮点常量,优先用
constexpr int MAX_SIZE = 100;:有类型、可调试、支持 constexpr 上下文 - 需要在编译期求值的表达式(比如数组长度),
constexpr是唯一可靠选择;#define SIZE 100无法参与模板推导或std::array声明 - 仅当需要条件编译(如
#ifdef DEBUG)或头文件卫士(#ifndef HEADER_H)时,才必须用#define - 宏不能重载、不能取地址;
const变量可以,哪怕只是为调试器留个名字
宏名全大写不是风格问题,是避免冲突的实际需要
预处理器不区分作用域,宏一旦定义,从定义点起全局生效,直到被 #undef 或文件结束。小写宏名极易和变量、函数撞车,而且错误提示往往晦涩难查。
- 所有
#define的标识符必须全大写加下划线,例如MAX_BUFFER_SIZE、ENABLE_LOGGING - 不要写
#define count 10——它会把后面所有int count = 0;替换成int 10 = 0;,报错信息里根本看不到count字样 - 头文件中定义的宏,务必用项目前缀隔离:
MYLIB_VERSION_MAJOR,防止第三方库冲突 - 用
gcc -E或clang -E预处理看展开结果,是排查宏污染最快的方式
宏定义末尾不能加分号,但很多人还是加了
加了分号的 #define LOG(x) printf("LOG: %d\n", x); 看似合理,但用在 if 分支里就会出问题:
立即学习“C++免费学习笔记(深入)”;
if (flag)
LOG(42);
else
puts("no log");实际展开为:
if (flag)
printf("LOG: %d\n", 42);;
else
puts("no log");中间多了一个空语句,else 就悬空了——编译可能过,逻辑却完全错乱。
- 宏体内部需要分号就写在内部,不要放在宏定义末尾
- 更安全的写法是用
do { ... } while(0)包裹多行宏:#define SAFE_LOG(x) do { printf("LOG: %d\n", x); fflush(stdout); } while(0) - 单行宏也别图省事加分号;检查方法很简单:把宏名替换成它的展开体,看语法是否还成立
宏的本质是文本替换,不是函数也不是语句。只要记住它发生在编译之前、没有任何语法校验、也不受作用域保护,大部分坑就绕得开了。











