跳转至

C 宏实用技巧笔记

1. 宏的基本用法

a. 常量宏

#define PI 3.14159
#define MAX_SIZE 1024
  • 编译前直接文本替换,不占内存
  • 缺点:无类型信息,调试困难
  • 建议:只在简单场景或条件编译中使用

b. 带参数宏

#define SQUARE(x) ((x)*(x))
#define MIN(a,b)  ((a)<(b)?(a):(b))
  • 必须加括号,避免优先级错误
  • 注意:参数可能被多次求值,例如
int a = 2;
int b = SQUARE(a++);  // 展开成 (a++ * a++), 非预期
  • 避坑方法:改用 inline 函数
inline int square(int x) { return x*x; }

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. 字符串化(#

#define TO_STRING(x) #x
printf("%s\n", TO_STRING(hello)); // "hello"

b. 拼接(##

#define CONCAT(a,b) a##b
int xy = 5;
printf("%d\n", CONCAT(x,y)); // 5

c. 条件编译

#ifdef DEBUG
#define LOG(msg) printf("DEBUG: %s\n", msg)
#else
#define LOG(msg)
#endif

4. 防坑写法总结

  1. 参数加括号:#define MUL(a,b) ((a)*(b))
  2. 避免副作用:inline 函数优于复杂宏
  3. 多行宏:do { ... } while(0) 包裹
  4. 避免宏嵌套副作用:不要写 #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)
  • 避免未使用警告
#define UNUSED(x) (void)(x)