C语言:预处理与宏定义详解
本文最后更新于:2022年9月4日 下午
预处理的步骤
C编译器做语法解析之前的预处理步骤:
- 对define语句进行替换
\
可以把分行的代码连接成一行- 将注释替换为一个空格
宏可被\
分割
#define STR “hello, “ \
“world”#define MAIN \
int main() \
{ \
return 0; \
}
内核中出现了大量的复杂宏定义,因此深入宏是十分必要的
条件预处理指示
两个示例:
1 |
|
1 |
|
宏定义
变量式宏定义(object-like macro)
宏定义名可以像变量一样在代码中使用,形式类似:#define STR "hello, world"
函数式宏定义(function-like macro)
宏定义可以像函数调用一样在代码中使用,形式类似:#define MAX(a, b) ((a)>(b)?(a):(b))
函数式宏定义的优势(相比较函数)
- 省去了分配和释放栈帧、传参、传返回值等一系列工作
- 简短并且被频繁调用的函数经常用函数式宏定义来代替实现。例如C标准库的很多函数都提供两种实现,一种是真正的函数实现,一种是宏定义实现
- inline关键字告诉编译器,这个函数的调用要尽可能快,可以当普通的函数调用实现,也可以用宏展开的办法实现
为了检查编译器对宏所做的替换操作,可以使用gcc -E命令
宏定义需要注意的事情:
- 宏可以重复定义,但是每次的定义均需相同。 如果需要重新定义一个宏,和原来的定义不同,可以先用#undef取消原来的定义,再重新定义
- 如果MAX被定义为
#define MAX(a, b) (a>b?a:b)
,那么MAX(i&0x0f, j&0x0f)将会被展开为(i&0x0f>j&0x0f?i&0x0f:j&0x0f)
而不是((i&0x0f)>(j&0x0f)?(i&0x0f):(j&0x0f))
,由于>结合优先级高于&,因此会出现很难找到的bug do{}while(0)
内核中宏定义常常写成:这里用do { … } while(0)括起来了,如果没有括起来:1
2
3
4
5#define device_init_wakeup(dev,val) \
do { \
device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val); \
} while(0)当使用以下if else语句时,会因多出一个1
2
3
4
5#define device_init_wakeup(dev,val) \
{device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val);}
if (n > 0)
device_init_wakeup(d, v);;
报错1
2
3
4if (n > 0)
device_init_wakeup(d, v);
else
continue;
#、##、可变参数
- 对于函数式宏,#可以将参数转换为字符串(并非简单的加“”,这里已经过转义)
- ##可以用于上面两种宏,可以将前后两个预处理Token连接成一个预处理Token
#define showlist(...) printf(#__VA_ARGS__)
- …表示可变参数,上面宏展开后为
printf("The first, second, and third items.");
- 在宏定义中,可变参数的部分用__VA_ARGS__表示
- …表示可变参数,上面宏展开后为
C语言:预处理与宏定义详解
http://gls.show/p/96b16a89/