Macro in C

作者:郑沛坤

宏属于C语言中预处理命令的一种,宏的优点是:

  • 方便对程序进行后期修改和裁剪;
  • 提高程序的运行效率,宏可实现函数的功能,又能避免函数的入栈和出栈操作,减小系统开销;
  • 宏由预处理器处理,可利用自己独特的语法实现C编译器无法实现的功能;

同时宏的缺点是:

  • 宏语句晦涩难懂,影响程序的可读性;
  • 由于宏语句是直接嵌入的,实现时所需的代码量会相对多一些;
  • 对带参的宏而言,预处理器不会检查参数是否合法,影响程序的可靠性;

宏包含obj-like和func-like两类,其中后者可以实现类似函数的功能。

1
2
3
4
5
6
//obj-like
#define 宏名 替换列表 换行符
#define pObj int *pObj
//func-like
#define 宏名([标识符列表]) 替换列表 换行符
#define pFunc(x,y) (x>y?x:y)
  1. 宏的语法
  • 宏以空白符分割不同部分,空白符的多少对于宏来说是没有意义的;

    1
    2
    3
    //两种表达方式效果是一样的
    #define PINT int pSign
    #define PINT int pSign
  • 宏定义以换行符结尾,意味着一个宏定义中无论多长,都只能有一个换行符,如果需要分行写,需要借助\;

    1
    2
    3
    4
    5
    #define MAIN	\
    int main() \
    { \
    return 0; \
    } \
  • 字符串化操作符#,”#”可以将参数转换为字符串进行处理;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #define WARN_IF(EXP) \
    if (EXP) \
    {\
    fprintf (stderr, "Warning: " #EXP "\n"); \
    }\
    //源码中
    WARN_IF (x == "0")
    //预处理后
    if (x == "0"){fprintf (__stderrp, "Warning: " "x == \"0\"" "\n");}
  • 粘贴操作符##,”##”可以将两个参数(token)合并为一个,给用户提供了一个动态生成token的能力;

    1
    2
    3
    4
    5
    #define GETTER(x, T) T get_ ## x() {return x;}
    //源码中
    GETTER(foo,const int)
    //预处理后
    const int get_foo() {return foo;}

    查看预处理器处理后的源码

    1
    gcc -E test.c -o macro//test.c即源码,macro即经过预处理器后的源码
  1. 宏的嵌套

    在宏的展开过程中,需要注意以下两点:

  • 在展开过程中,如果替换列表中出现了被展开宏,那么该被展开宏将不再展开;

  • 在每次展开过程中会创建一个”蓝色集合”,展开过的宏会放入蓝色集合中,以后的展开过程中,蓝色集合中的宏将不再继续展开;

    图1

1
2
3
4
5
6
7
#define foo foo a bar b bar baz c
#define bar foo 12
#define baz bar 13
//源码中
foo
//预处理后
foo a foo 12 b foo 12 foo 12 13 c

http://feng.zone/2017/05/18/%E5%AE%8F%E5%AE%9A%E4%B9%89%E9%BB%91%E9%AD%94%E6%B3%95-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%A5%87%E6%8A%80%E6%B7%AB%E5%B7%A7-2/

  1. Linux常用宏

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //CONCAT宏:连接两个参数
    #define _CONCAT(a,b) a##b
    //TYPECHECK宏:检查x是否为type类型
    #define typecheck(type,x) \
    ({ type __dummy; \
    typeof(x) __dummy2; \
    (void)(&__dummy == &__dummy2); \
    1; \
    })
    //SWAP宏:交换两个变量
    #define swap(a, b) \
    do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
    //OFFSETOF宏:计算type结构体内member成员的偏移位置
    #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
    //CONTAINER_OF宏:计算type结构体首地址值
    #define container_of(ptr, type, member) ({ \
    const typeof(((type*)0)->member)* __mptr = (ptr); \
    (type*)((char*)__mptr - offsetof(type, member)); })