1‘#’ 和 ‘##’ 属于预处理标记。‘#’ 和 ‘##’ 用于类似函数的宏定义中(或者简称为宏定义函数)。2‘__VA_ARGS__’ 是 C99 引入的用于支持宏定义函数中使用可变参数。
操作符 ‘#’
在宏定义展开的时候,标记 ‘#’ 用于将 ‘#’ 后面的宏定义函数中的参数转化为对应的字符串。宏定义函数的参数与预处理标记 ‘#’ 之间出现的每一个空格都会被删除,并删除第一个预处理标记之前和最后一个预处理标记之后的空白字符,但是宏定义函数参数中的空格会保留。
其中,空参数转化为为空,即宏定义函数入参为空,那么展开的时候也为空。
上面的这段话比较难理解,这里为了准确地传达其意义,我们来看一个示例程序。
在看到代码后,可以先猜猜可能的输出结果,如果你答对了,那就是真的会了!
注意,这里我基于 RT-Thread QEMU BSP 进行代码展示,代码真实编译通过,运行正常。
示例程序 A
请看以下代码:
1#include 2#include 3 4#define mkstr(var) (#var) 5 6int main(void) 7{ 8 rt_kprintf("hello rt-thread"); 910 rt_kprintf(mkstr(hello rt-thread));1112 return 0;13}
请问:
- 它能编译通过吗?
- 它能输出什么内容?
答案:
- 它可以正常编译通过
- 它输出的内容1hello rt-thread
2hello rt-threadmsh />
从上面输出的信息可以看到,hello rt-thread 字符串被准确地输出到了控制台,但是没有增加回车换行。其中 msh /> 字符串是 RT-Thread 控制台回显。
如上,代码 rt_kprintf(mkstr(hello rt-thread)); 中的 hello rt-thread 在没有加引号的情况下,被转化成了字符串。
示例程序 B
为示例程序 A 打印的字符串增加回车换行。
1#include 2#include 3 4#define mkstr(var) (#var) 5 6int main(void) 7{ 8 rt_kprintf("hello rt-thread"); 910 rt_kprintf(mkstr(hello rt-thread));1112 return 0;13}
有了示例 A 的基础,示例 B 那就是 soeasy,直接在原有的基础上增加 转义字符即可输出回车换行。
输出结果如下: