宏函数(带参宏)
#define ADD(A,B) ((A)+(B))
✅ 要点:
● 无类型,不做类型检查
● 参数 A、B 各自加括号,整体表达式也要套括号,防止运算符优先级灾难
● 末尾不能加分号,加分号会把;一并替换进代码
普通函数 vs 宏函数
| 对比项 | 普通函数 Add(int a, int b) | 宏函数 ADD(A,B) |
|---|---|---|
| 处理时机 | 编译阶段,生成独立函数,调用时 call 跳转 | 预处理阶段文本替换,没有函数实体 |
| 调用开销 | 参数传递、call/ret、栈操作,有开销 | 无函数调用开销,直接原地展开代码 |
| 类型检查 | 有,参数类型不匹配直接报错 | 无,随便传,编译不报错,容易出诡异 bug |
| 返回值 | 有固定返回类型 | 只是文本替换,没有返回值概念 |
| 调试 | 可以断点进入函数内部 | 不能断点,宏展开后不存在这个 "函数" |
错误示范(不加括号)
c
#define BAD_ADD(A,B) A+B
// 调用:BAD_ADD(1,2)*10
// 替换后:1+2*10 → 结果21,预期30,优先级出错
c
#define BAD_ADD(A,B) (A+B)
// 调用:BAD_ADD(1||2,3) ? 1 :0 ,缺少外层括号也会出问题
宏函数典型坑点
坑 1:参数带自增 / 自减
c
#define ADD(A,B) ((A)+(B))
int a=1;
int res = ADD(a++, a);
// 展开:((a++) + (a)),a++会执行两次,结果不符合预期
宏只是文本替换,参数如果带有副作用(++、--、函数调用),会多次执行!普通函数不会
坑 2:末尾加分号
c
#define ADD(A,B) ((A)+(B));
int r = ADD(1,2);
//替换后 int r = ((1)+(2)); ; 多一个空语句,部分场景语法报错
坑 3:无类型检查
c
ADD("123",123);
不会报错,直接计算字符串字面量地址 + 123,得到一个无意义数字
什么时候用宏函数,什么时候用普通函数
✅ 适合宏:极简短运算,求最大最小值、简单位运算,追求极小开销
❌ 不要宏:代码逻辑复杂、参数有 ++ 这类副作用、需要断点调试、需要类型安全 → 用内联函数inline
补充:inline 内联函数(用来替代宏函数)
C99 引入,兼顾两者优点:
- 有类型检查
- 编译器原地展开,没有 call 调用开销
- 可以断点调试
c
inline int Add(int a,int b)
{
return a+b;
}