C语言代码的常用技巧

描述

#和##对于大部分C语言玩得还算比较溜的朋友并不是很陌生,不过能把这两个知识点游刃有余的应用到所在代码中的每个角落,似乎并没有几个人能够做到,学的时候朗朗上口,而编码的时候却抛之脑后。

但是今天bug菌还是想重新介绍这两个“兄弟”,希望大家能够写出"秀"一点的代码~

1#和##基础

对于这两个语法的功能都比较简单,且都是在预处理阶段做一些工作 :

#主要是将宏参数转化为字符串

##主要是将两个标识符拼接成一个标识符

没点代码似乎并不是那么形象 : 

参考demo:

 

 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    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#include 
 2#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}
  暗自欢喜的编译着,然而却得到了如下结果:

字符串

得到的并不是拼接以后你想要的uart1,难道不能这么玩?当然不是,不然也不会在这里拿出来说 。 首先要知道原因 : 进行宏定义嵌套的情况,#或者##仅在当前宏有效,嵌套宏中不会再次展开,既然当前宏无法展开,那么我只能再加一级宏定义作为转换宏进行展开,看能不能解决该问题:
 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#include 
 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}
  2、统一宏替换

拼接标识符意味着符号的粒度更高,而这碎片化的符号进行有效的管理,就可以使得符号更加具有通用性和灵活性。 其实这种思想跟我们代码模块话是同样的道理。 来首先我们用一个两层拼接体验一下:
 1#include 
 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}
    编写的思路bug菌在代码中跟大家都标注了,相信大家一眼就能看懂,似乎并没有想象中那么难。 而在前面介绍##的基础知识提过,只要转换宏写得够多,你可以一层套一层,最终获得你想要的标识符,达到修改一个简单的宏即可替换一整套宏的效果。 所以关键还是你要清晰的把拼接变量找出来,bug菌这里仅展示了一个拼接变量,当然多个也是同样没有问题的,跟我们函数传递参数一样,不过这样也会增加整个替换的复杂度,合理利用即可~

  审核编辑:汤梓红

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分