0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C语言代码的常用技巧

TopSemic嵌入式 来源: 最后一个bug 2023-12-05 09:25 次阅读

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

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

1#和##基础

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

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

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

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

参考demo:

 1#include
 2#include
 3
 4//#的简单使用
 5#defineSTR(str)#str
 6
 7//##的简单使用
 8#defineCMB(a,b)a##b
 9
10intmain(intargc,char*argv[]){
11
12intCMB(uart,1)=5;
13intCMB(uart,2)=10;
14
15printf("#的简单使用:
");
16printf("%s
",STR(3.1415));
17printf("%s
",STR(abcd));
18
19printf("##的简单使用:
");
20printf("%d
",uart1);
21printf("%d
",uart2);
22
23return0;
24}

输出结果:

de3e1544-9301-11ee-939d-92fbcf53809c.png

从结果上看来似乎#仅仅只是代替了字符串的双引号,而##却实现了标识符的拼接,这样就为编码标识符的处理上能够带来更多的可玩性。 那么,下面bug菌跟大家具体展示一下他们的常用技巧:

2#的玩法

1、标识符的“字符串变量"

“#”一般结合打印语句组合成一个宏定义,可以方便的打印相关信息,下面给个简单的实例就明白了。

 1#include
 2#include
 3
 4//#打印调试
 5#defineDebugLogExpr(Expr)printf("%s:%d
",#Expr,Expr);
 6
 7//私有参数访问
 8intsFucntion(void)
 9{
10staticintvar=10;
11returnvar;
12}
13
14intmain(intargc,char*argv[]){
15
16intDebugVar=50;
17
18DebugLogExpr(DebugVar);//直接打印变量名和变量
19DebugLogExpr(100/5);//打印表达式及结果
20DebugLogExpr(sFucntion());//打印相关函数名及结果
21
22return1;
23}

输出结果:

de4b02fe-9301-11ee-939d-92fbcf53809c.png  

这样的话就不需要总是采用双引号来单独书写,同时你还可以继续扩展构造更加灵活的宏。

2、结合##进行字符串拼接打印


前面介绍了##进行标识符的拼接,那么实现拼接标识符转化为字符串看来很简单吧,于是你会编写了如下代码:

 1#include
 2#include
 3
 4//#的简单使用
 5#defineSTR(str)#str
 6
 7//##的简单使用
 8#defineCMB(a,b)a##b
 9
10intmain(intargc,char*argv[]){
11
12intCMB(uart,1)=5;
13
14printf("%s
",STR(CMB(uart,1)));
15
16return0;
17}
暗自欢喜的编译着,然而却得到了如下结果:

de595b1a-9301-11ee-939d-92fbcf53809c.png

得到的并不是拼接以后你想要的uart1,难道不能这么玩?当然不是,不然也不会在这里拿出来说 。 首先要知道原因 : 进行宏定义嵌套的情况,#或者##仅在当前宏有效,嵌套宏中不会再次展开,既然当前宏无法展开,那么我只能再加一级宏定义作为转换宏进行展开,看能不能解决该问题:
 1#include
 2#include
 3
 4//#的简单使用
 5#defineSTR(str)#str
 6
 7//##的简单使用
 8#defineCMB(a,b)a##b
 9
10#defineSTR_CON(str)STR(str)//转换宏
11
12intmain(intargc,char*argv[]){
13
14intCMB(uart,1)=5;
15
16printf("%s
",STR_CON(CMB(uart,1)));
17
18return0;
19}
此时输出的结果符合我们的预期:

de6decb0-9301-11ee-939d-92fbcf53809c.png

首先进行第一层转换宏替换处理掉##拼接符得到str(uart1),然后进行字符串转换符的处理为uart1字符串打印输出,当然以后你会遇到一些复杂的,不过要诀就是宏替换只会处理当前的#或者##,否则就需要增加转换宏提前进行宏替换展开。 所以采用##拼接出来的标识符想要打印输出的话,使用#进行转换是最直接、方便的。

3##的玩法

##拼接符的玩法有点多,甚至有些还比较绕,当然如果你游刃有余的话,这对于重构代码是一把“ 利器 ”。

1、在结构体定义中的妙用

下面是bug菌经常在项目代码中用到的##结构体定义法,也是非常多开源代码中惯用的做法,相比常规的结构体定义法,确实省去很多重复的代码。 比如下面的参考代码 :

 1#include
 2#include
 3
 4#defineDF_STRUCT(name)typedefstructtag##namename;
 5structtag##name
 6
 7DF_STRUCT(DevManage)
 8{
 9intindex;//索引
10intAccess;//权限
11//...
12};
13
14intmain(intargc,char*argv[]){
15
16DevManagestDevManage;
17
18stDevManage.index=1;
19stDevManage.Access=666;
20
21printf("DevIndex:%d
",stDevManage.index);
22printf("DevAccess:%d
",stDevManage.Access);
23
24return1;
25}
2、统一宏替换

拼接标识符意味着符号的粒度更高,而这碎片化的符号进行有效的管理,就可以使得符号更加具有通用性和灵活性。 其实这种思想跟我们代码模块话是同样的道理。 来首先我们用一个两层拼接体验一下:
 1#include
 2#include
 3
 4//假如这是stm32库中的宏
 5#defineGPIO_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菌这里仅展示了一个拼接变量,当然多个也是同样没有问题的,跟我们函数传递参数一样,不过这样也会增加整个替换的复杂度,合理利用即可~

审核编辑:汤梓红

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

    关注

    180

    文章

    7534

    浏览量

    128916
  • 字符串
    +关注

    关注

    1

    文章

    552

    浏览量

    20151
  • C代码
    +关注

    关注

    1

    文章

    89

    浏览量

    14177
  • 结构体
    +关注

    关注

    1

    文章

    125

    浏览量

    10751

原文标题:同事C代码中的#、##把我秀了~

文章出处:【微信号:TopSemic,微信公众号:TopSemic嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言常用宏定义技巧(转)

    C语言常用宏定义技巧
    发表于 07-26 12:47

    msp430常用C语言模块

    msp430常用C语言模块
    发表于 08-17 15:48

    MATLAB语言常用算法程序集 源代码

    MATLAB语言常用算法程序集 源代码
    发表于 08-17 16:57

    单片机常用C语言代码

    51单片机常用C语言代码
    发表于 12-25 18:51

    常用C语言代码

    常用c语言代码
    发表于 04-25 15:55

    单片机C语言常用算法

    单片机C语言常用算法
    发表于 10-03 16:58

    C语言常用排序方法大全

    C语言常用排序方法大全
    发表于 11-07 17:10

    C语言常用的预处理命令

    1.基础知识(1)C语言常用的预处理命令——typedef具体可见C语言基础语法知识。(2)延时函数(知识粗略估计延时时间,若要精确延时,需
    发表于 07-14 07:10

    STM32常用c语言有哪些

    STM32常用c语言有哪些?GPIO的工作原理是什么?GPIO的相关配置寄存器包括哪些?
    发表于 10-14 06:41

    C语言常用的开发工具有哪些?

    C语言常用的开发工具有哪些?
    发表于 10-18 07:46

    C语言实现常用排序算法是什么?

    C语言实现常用排序算法是什么?
    发表于 10-19 06:41

    C语言代码的风格有哪些

    C语言代码的命名风格是怎样的?C语言代码的程序风格是怎样的?
    发表于 02-25 07:21

    c语言常用算法

    非常实用的《c语言常用算法程序集》针对工程中常用的行之有效的算法而编写,其主要内容包括多项式的计算、复数运算、随机数的产生、矩阵运算、矩阵特征值与特征向量的计算、线性代数方程组的求解、
    发表于 04-11 16:41

    常用的FPGA代码

    常用的FPGA代码,VHDL语言编写。需要请下载
    发表于 05-26 11:36 16次下载

    源码-易语言常用算法

    语言是一门以中文作为程序代码编程语言学习例程:易语言-源码-易语言常用算法
    发表于 06-06 17:43 4次下载