华丽的开头:
在一些开源的软件中经常出现#和##得用法, 因此搞清楚其语意就变得很重要,尤其是一些C语言功底并不是那么扎实的童鞋来说(比如我)。其实#和##的作用可以统一用一句话来概括,"使得变量的组织和运用变得更加灵活", 有些读者可能刚开始无法理解,没关系,通过本文,相信您对#和##一定有一个深入的认识。好了,进入正题。
#的用法
解释:一般#是将右侧宏参数格式化为一个字符串
实例1:
#define T(X) #X
int main(void)
{
printf("T(x):\"%s\"\r\n", T(A));
printf("T(x):\"%s\"\r\n", T("A"));
printf("T(x):\"%s\"\r\n", T('A'));
while (true);
return 0;
}
大家猜猜打印的结果是什么,此处冥想10秒钟。。。。。。如下
其输出结果为
可以看出 :不管A是字符串 或者是 数字 再或者 没有定义, #X的结果都将是将X转换为字符串"X"
哪有的人就要问了,要是A被定义了,但是A是一个宏,那还会不会如上一样直接转换为"A"呢? 我们继续实验
实验2
#define A 129
#define T(X) #X
int main(void)
{
printf("T(x):\"%s\"\r\n", T(A));
while (true);
return 0;
}
这次的结果
我们可以看到,结果依旧是"A",而不是"129" ,这是不是说明#在做宏展开的时候并不会展开其内部的宏参数呢。然而并不是都是这样,这仅仅局限于一次宏展开。
实例3
#define A (129)
#define T(X) #X
#define TT(X) T(X)
int main(void)
{
printf("T(x):\"%s\"\r\n", TT(A));
while (true);
return 0;
}
按上面的结论,此时结果应该依旧是"A",然而并不是这么简单的。
这是为什么呢?让我们来看一看预编译时的宏展开过程。
TT(A) ---> T(A) ----> T((129)) ---> #(129) --> "(129)"
T(A) ---- #A ---- > "A"
此时我们可以得出结论:
#只会将宏参数直接格式化为字符串,如果一级展开发现其含有# 那么此时将不会对宏进行展开,直接转为字符串,如果不存在且其实参任然是宏参数,则对内部宏参数进行替换。
##的用法
解释:##一般用来连接两个特定的对象,并组成一个新的字符串或者变量,也就是将两个对象粘合在一次。
大家是不是看到这个解释都被绕迷糊了,没关系,下面会给出一些简单的实例代码进行解释。
实例1:
#define GPIOA 0X10
#define RCC_APB2Periph_GPIOA 0X0101
#define RCC_CLK(X) RCC_APB2Periph_##X
int main(void)
{
printf("0X%X\r\n", RCC_CLK(GPIOA));
while (1);
return 0;
}
相信大家都猜到了结果,没错 结果就是0X0101,这说明##的参数格式化过程中同样不存在参数的展开,如果结果必然无法运行通过,因为其试图打印RCC_APB2Periph_0X10,这显然是错误的。
同样,按照上面的思维,因为##处在以及宏展开的位置,是不是由于这个原因导致的宏参数没有被展开呢?带着这样的疑惑,我们继续进行探索实验。
实例2
#include <stdio.h>
#define GPIOA 0X10
#define RCC_APB2Periph_GPIOA 0X0101
#define RCC_CLK(X) RCC_APB2Periph_##X
#define RCC_CLK_COPY(X) RCC_CLK(X)
int main(void)
{
printf("0X%X\r\n", RCC_CLK_COPY(GPIOA));
while (1);
return 0;
}
猜测:
1、如果宏参数一直不会展开,那么此时打印的结果应该与上面的一样 为0X0101
2、如果宏参数展开了,那么此时必定是报RCC_APB2Periph_0X10未定义什么的
好了 我们直接看结果
结果完全符合了猜想2,因此我们得出以下结论。
结论
进行宏操作的时候,如果一级宏展开遇到#或者##,那么即使此时的实参任然是宏参数也不会进行宏展开,而是直接进行(#格式化为字符串,##直接连接到其他部分)操作,否则,将依次对内部宏参数进行展开