预定义宏

  1. __LINE__ // 当前行号
  2. __FILE__ // 当前文件名
  3. __FUNCTION__ // 当前函数名

带参数的宏

宏定义各部分的名称约定:
宏 - 图1

基本展开规则:

  1. 如果替换语句中含有宏定义,先展开;与 ## 参与运算的除外
  2. 如果实参是宏定义,先展开,与 ### 参与运算的除外


省略参数 ...

a. 基本使用:省略号必须位于参数列表的末尾

  1. // 使用方法1:不指定省略参数的名称
  2. #define FOO(...) foo(__VA_ARGS__)
  3. // 使用方法2:指定省略参数的名称
  4. #define FOO(args...) foo(args)

b. 省略参数为空,前方有逗号,需要使用 ##

  1. // 错误
  2. #define LOG(format, args...) fprintf(stderr, format, args)
  3. LOG("test"); // 展开为:fprintf(stderr, "test", );
  4. // 正确
  5. #define LOG(format, args...) fprintf(stderr, format, ##args)
  6. LOG("test"); // 展开为:fprintf(stderr, "test");

字符串化运算符 #

使用 # 符号可以将实参字符串化
注:宏的参数也是参数,需要符合文法,例如引号需要成对
a. 如果实参是另一个宏定义符号,那么这个符号不会被展开

  1. #define PUTS(arg) puts(#arg)
  2. #define ABC abc
  3. // 这样的使用:
  4. PUTS(ABC);
  5. // 被展开为:
  6. puts("ABC");

b. 如果实参是字符串,那么宏展开时:除了添加双引号,还会在原有的 "\ 前面加上 \

  1. #define PUTS(arg) puts(#arg)
  2. // 这样使用:
  3. PUTS("abc\n");
  4. // 被展开为:
  5. puts("\"abc\\n\"");

c. 如果实参不是字符串,那么宏展开时,只会添加双引号
注意这里的转义字符

  1. #define PUTS(arg) puts(#arg)
  2. // 这样使用:
  3. PUTS(abc\n);
  4. // 被展开为:
  5. puts("abc\n");

d. 对省略参数进行字符串化,会在参数之间添加 ,,连续的空格会被替换为一个空格

  1. #define PUTS(...) puts(#__VA_ARGS__)
  2. // 这样使用:
  3. PUTS(abc, 123,456);
  4. // 被展开为:
  5. puts("abc, 123,456");

记号粘贴运算符 ##

a. ## 作用的过程:

  1. 将实参带入,合并 ## 左右两侧的内容,得到一个新的 token
  2. 尝试展开新得到的 token

注:如果替换语句得 ## 两侧含有宏定义,不会单独展开;如果实参是宏定义,也不会单独展开

  1. #define BOX_FIRE "FUKA"
  2. #define box_fire "fuka"
  3. #define BOX box
  4. #define FIRE fire
  5. // 替换语句的 ## 两侧有宏定义 BOX,不会单独展开
  6. #define PRINT(arg) printf(BOX ## _ ## arg)
  7. // 实参是宏定义 FIRE,不会单独展开
  8. PRINT(FIRE);
  9. /**
  10. @最终展开:
  11. printf("FUKA");
  12. @解析:
  13. 可以看到:宏定义中的 BOX 和实参中的 FIRE 都没有被展开
  14. 通过 ## 得到 BOX_FIRE 后,再展开为 "FUKA"
  15. */

b. ## 两侧可以有空格,特定情况下参数可以为空

  1. #define FOO(arg) printf("123" ## arg)
  2. // 这样使用:
  3. FOO();
  4. // 被展开为:
  5. printf("123");

使用示例

a. 打印日志

  1. #define err_log(format, args...) \
  2. do { printf("\033[36m""[%s, %s, %d]: "format"\033[0m\n", __FILE__, __FUNCTION__, __LINE__, ##args); }while(0)