#和##对于大部分C语言玩得还算比较溜的朋友并不是很陌生,不过能把这两个知识点游刃有余的应用到所在代码中的每个角落,似乎并没有几个人能够做到,学的时候朗朗上口,而编码的时候却抛之脑后。
但是今天bug菌还是想重新介绍这两个“兄弟”,希望大家能够写出"秀"一点的代码~
1#和##基础
对于这两个语法的功能都比较简单,且都是在预处理阶段做一些工作 :
#主要是将宏参数转化为字符串
##主要是将两个标识符拼接成一个标识符
没点代码似乎并不是那么形象 :
参考demo:
1#include2#include 3 4//#的简单使用 5#define STR(str) #str 6 7//##的简单使用 8#define CMB(a,b) a##b 9 10int main(int argc, char *argv[]) { 11 12 int CMB(uart,1) = 5; 13 int CMB(uart,2) = 10; 14 15 printf("#的简单使用: "); 16 printf("%s ",STR(3.1415)); 17 printf("%s ",STR(abcd)); 18 19 printf("##的简单使用: "); 20 printf("%d ",uart1); 21 printf("%d ",uart2); 22 23 return 0; 24}
输出结果:
从结果上看来似乎#仅仅只是代替了字符串的双引号,而##却实现了标识符的拼接,这样就为编码标识符的处理上能够带来更多的可玩性。 那么,下面bug菌跟大家具体展示一下他们的常用技巧:
2#的玩法
1、标识符的“字符串变量"
“#”一般结合打印语句组合成一个宏定义,可以方便的打印相关信息,下面给个简单的实例就明白了。
1#include2#include 3 4//#打印调试 5#define DebugLogExpr(Expr) printf("%s : %d ",#Expr, Expr); 6 7//私有参数访问 8int sFucntion(void) 9{ 10 static int var = 10; 11 return var; 12} 13 14int main(int argc, char *argv[]) { 15 16 int DebugVar = 50; 17 18 DebugLogExpr(DebugVar); //直接打印变量名和变量 19 DebugLogExpr(100/5); //打印表达式及结果 20 DebugLogExpr(sFucntion()); //打印相关函数名及结果 21 22 return 1; 23}
输出结果:
这样的话就不需要总是采用双引号来单独书写,同时你还可以继续扩展构造更加灵活的宏。
2、结合##进行字符串拼接打印
前面介绍了##进行标识符的拼接,那么实现拼接标识符转化为字符串看来很简单吧,于是你会编写了如下代码:
1#include暗自欢喜的编译着,然而却得到了如下结果:2#include 3 4//#的简单使用 5#define STR(str) #str 6 7//##的简单使用 8#define CMB(a,b) a##b 9 10int main(int argc, char *argv[]) { 11 12 int CMB(uart,1) = 5; 13 14 printf("%s ",STR(CMB(uart,1))); 15 16 return 0; 17}
1 #include此时输出的结果符合我们的预期:2#include 3 4//#的简单使用 5#define STR(str) #str 6 7//##的简单使用 8#define CMB(a,b) a##b 9 10#define STR_CON(str) STR(str) //转换宏 11 12int main(int argc, char *argv[]) { 13 14 int CMB(uart,1) = 5; 15 16 printf("%s ",STR_CON(CMB(uart,1))); 17 18 return 0; 19}
首先进行第一层转换宏替换处理掉##拼接符得到str(uart1),然后进行字符串转换符的处理为uart1字符串打印输出,当然以后你会遇到一些复杂的,不过要诀就是宏替换只会处理当前的#或者##,否则就需要增加转换宏提前进行宏替换展开。 所以采用##拼接出来的标识符想要打印输出的话,使用#进行转换是最直接、方便的。
3##的玩法
##拼接符的玩法有点多,甚至有些还比较绕,当然如果你游刃有余的话,这对于重构代码是一把“ 利器 ”。
1、在结构体定义中的妙用
下面是bug菌经常在项目代码中用到的##结构体定义法,也是非常多开源代码中惯用的做法,相比常规的结构体定义法,确实省去很多重复的代码。 比如下面的参考代码 :
1#include2、统一宏替换2#include 3 4#define DF_STRUCT(name) typedef struct tag##name name; 5 struct tag##name 6 7DF_STRUCT(DevManage) 8{ 9 int index; //索引 10 int Access; //权限 11 //... 12}; 13 14int main(int argc, char *argv[]) { 15 16 DevManage stDevManage; 17 18 stDevManage.index = 1; 19 stDevManage.Access = 666; 20 21 printf("Dev Index :%d ",stDevManage.index ); 22 printf("Dev Access:%d ",stDevManage.Access ); 23 24 return 1; 25}
1#include编写的思路bug菌在代码中跟大家都标注了,相信大家一眼就能看懂,似乎并没有想象中那么难。 而在前面介绍##的基础知识提过,只要转换宏写得够多,你可以一层套一层,最终获得你想要的标识符,达到修改一个简单的宏即可替换一整套宏的效果。 所以关键还是你要清晰的把拼接变量找出来,bug菌这里仅展示了一个拼接变量,当然多个也是同样没有问题的,跟我们函数传递参数一样,不过这样也会增加整个替换的复杂度,合理利用即可~2#include 3 4//假如这是stm32库中的宏 5#define GPIO_Pin_0 ((int)0x0001) /*!< Pin 0 selected */ 6#define GPIO_Pin_1 ((int)0x0002) /*!< Pin 1 selected */ 7#define GPIO_Pin_2 ((int)0x0004) /*!< Pin 2 selected */ 8#define GPIO_Pin_3 ((int)0x0008) /*!< Pin 3 selected */ 9 10#define USART1 ((int *) 0x1000) 11#define USART2 ((int *) 0x2000) 12 13 14//拼接变量 15#define UARTX 1 16 17//最终的组合标识符 18#define UART1_CORE USART1 19#define UART1_RX GPIO_Pin_0 20#define UART1_TX GPIO_Pin_1 21 22#define UART2_CORE USART2 23#define UART2_RX GPIO_Pin_2 24#define UART2_TX GPIO_Pin_3 25 26//拼接过程 27#define _UARTX_CORE(uartx) UART##uartx##_CORE 28#define UARTX_CORE(uartx) _UARTX_CORE(uartx) 29 30 31#define _UARTX_RX(uartx) UART##uartx##_RX 32#define UARTX_RX(uartx) _UARTX_RX(uartx) 33 34#define _UARTX_TX(uartx) UART##uartx##_TX 35#define UARTX_TX(uartx) _UARTX_TX(uartx) 36 37 38int main(int argc, char *argv[]) { 39 40 //组合标识符的使用 41 printf("0x%x ",UARTX_CORE(UARTX)); 42 printf("0x%x ",UARTX_RX(UARTX)); 43 printf("0x%x ",UARTX_TX(UARTX)); 44 45 return 1; 46}
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !