C 宏实用技巧笔记
1. 宏的基本用法
a. 常量宏
- 编译前直接文本替换,不占内存
- 缺点:无类型信息,调试困难
- 建议:只在简单场景或条件编译中使用
b. 带参数宏
- 必须加括号,避免优先级错误
- 注意:参数可能被多次求值,例如
- 避坑方法:改用
inline函数
2. 多行宏与 do { ... } while(0)
为什么要用 do { ... } while(0)?
- 让宏展开后看起来就是一个完整的单语句
- 避免
if/else配对错误
例子(错误情况):
#define TEST_ASSERT(cond, msg) \
if (cond) printf("OK\n"); \
else printf("FAIL\n");
if (x > 0)
TEST_ASSERT(x == 1, "x");
else
printf("x<=0\n");
宏展开后会导致 else 和内部 if 错配,编译或逻辑错误。
正确写法:
#define TEST_ASSERT(cond, msg) do { \
if (cond) { \
printf("✓ %s\n", msg); \
tests_passed++; \
} else { \
printf("✗ %s\n", msg); \
tests_failed++; \
} \
} while(0)
宏展开后就是一个完整的 do { ... } while(0);,外层 if/else 能正确匹配。
为什么要加 \?
- C 预处理器的宏 不能直接换行
\放在行尾表示“这一行和下一行是同一个宏定义的一部分”
3. 高级技巧
a. 字符串化(#)
b. 拼接(##)
c. 条件编译
4. 防坑写法总结
- 参数加括号:
#define MUL(a,b) ((a)*(b)) - 避免副作用:
inline函数优于复杂宏 - 多行宏:
do { ... } while(0)包裹 - 避免宏嵌套副作用:不要写
#define INC(x) (++x)这种
5. 常用宏模板
- 日志宏
#define LOG_INFO(msg) printf("[INFO] %s\n", msg)
#define LOG_ERROR(msg) fprintf(stderr, "[ERROR] %s\n", msg)
- 测试宏
#define TEST_ASSERT(cond,msg) do { \
if (cond) { printf("[PASS] %s\n", msg); } \
else { printf("[FAIL] %s\n", msg); } \
} while(0)
- 避免未使用警告