inline是类型安全的函数,有作用域和调试信息;#define是无类型检查的纯文本替换,易引发副作用、重复计算和类型错误,仅在字符串化、拼接等场景不可替代。

inline 是函数,#define 是文本替换
这是最根本的区别:inline 修饰的是真正的函数,有类型检查、作用域和调试信息;而 #define 宏只是预处理器在编译前做的纯文本替换,不经过编译器语义分析。
比如写 #define SQUARE(x) x * x,调用 SQUARE(a + b) 会变成 a + b * a + b,结果完全错误;而 inline int SQUARE(int x) { return x * x; } 没这个问题,参数求值只发生一次,且类型安全。
常见错误现象:
-
#define MAX(a,b) (a > b ? a : b)在MAX(i++, j++)中导致 i 和 j 各自递增两次 -
inline函数传入表达式(如i++)时,行为与普通函数一致,副作用只发生一次
inline 不保证内联,#define 总是“展开”
inline 只是一个建议,编译器有权拒绝内联——比如函数体过大、含循环或递归、启用了 -O0 优化关闭时。而 #define 无论多复杂,都会无条件做文本复制,可能造成目标码膨胀或重复计算。
立即学习“C++免费学习笔记(深入)”;
使用场景差异:
- 调试阶段想单步进入函数逻辑?
inline函数可以(尤其加了-g且未被优化掉),#define宏不行 - 需要取函数地址?
&my_inline_func合法(若未被内联),&MY_MACRO直接报错 - 模板推导或重载解析?
inline函数参与,#define宏不参与
类型安全与调试支持差距明显
inline 函数参数和返回值有明确类型,编译器能做隐式转换检查、const 正确性校验、甚至 SFINAE;#define 宏对类型一无所知,容易掩盖类型错误。
例如:
inline double sqrt_safe(double x) { return x >= 0 ? std::sqrt(x) : 0; } #define SQRT_SAFE(x) ((x) >= 0 ? std::sqrt(x) : 0)
如果传入 int*,inline 版本直接编译失败,#define 版本可能静默转成整数比较再调用 std::sqrt,引发未定义行为。
性能影响方面:
- 现代编译器对
inline的决策比人工更可靠,盲目用#define替代可能反而阻碍优化(如妨碍 LTO 或跨函数分析) -
#define展开后可能产生冗余表达式,而inline函数体可被编译器复用公共子表达式
什么时候还不得不写 #define
不是所有场景 inline 都能替代 #define。以下情况仍需宏:
- 需要字符串化(
#x)或拼接(x ## y):比如日志宏#define LOG(x) std::cout - 跨编译单元的配置开关:
#ifdef DEBUG这类预处理指令无法用函数模拟 - 生成不同签名的重载集(如变参宏
LOGF(fmt, ...)),c++20 前inline无法做到
但注意:C++11 起应优先用 constexpr 函数替代纯计算宏;C++17 引入 inline 变量,让全局常量定义不再依赖 #define;宏该退居为“元编程辅助角色”,而非逻辑实现主力。
最容易被忽略的一点:头文件里定义 inline 函数是安全的(多次包含不会违反 ODR),但 #define 宏一旦在多个地方重复定义(哪怕内容相同),就可能触发编译器警告甚至错误——尤其在模块化或子项目合并时。