预处理器陷阱
预处理器的文本替换机制虽然强大,但也隐藏了许多陷阱。副作用多次求值、优先级问题、分号问题是最常见的三类错误。理解这些陷阱,可以写出更安全的宏。
副作用多次求值
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = 5;
int m = MAX(x++, 3);
/* 展开:((x++) > (3) ? (x++) : (3)) */
/* x 自增了两次! */
解决方案:用 inline 函数
static inline int max_int(int a, int b)
{
return (a) > (b) ? (a) : (b);
}
int m = max_int(x++, 3); /* x 只自增一次 */
优先级问题
#define SQUARE(x) (x * x)
int s = SQUARE(3 + 4); /* 3 + 4 * 3 + 4 = 19 */
/* 正确 */
#define SQUARE(x) ((x) * (x))
#define ADD(a, b) (a) + (b)
int r = ADD(1, 2) * 3; /* (1) + (2) * 3 = 7 */
/* 正确 */
#define ADD(a, b) ((a) + (b))
分号问题
#define PRINT(x) printf("%d\n", (x));
if (condition)
PRINT(42); /* printf(...); */
else /* else 没有匹配的 if */
/* ... */
/* 正确 */
#define PRINT(x) do { \
printf("%d\n", (x)); \
} while(0)
递归展开
#define FOO FOO
FOO /* 不递归展开,保持 FOO */
#define A B
#define B A
A /* 展开为 B,不再继续 */
常见错误
宏中定义变量:
#define SWAP(a, b) { \
int temp = (a); \
(a) = (b); \
(b) = temp; \
}
/* 如果 a 或 b 是 float,temp 是 int → 截断 */
/* 正确:用 typeof(GCC 扩展)或传类型参数 */
#define SWAP(type, a, b) { \
type temp = (a); \
(a) = (b); \
(b) = temp; \
}
最佳实践
- 宏参数和整体用括号包围
- 避免在宏参数中使用自增/自减
- 多语句宏用
do { ... } while(0) - 复杂逻辑用
inline函数替代 - 用编译器警告检测宏问题