C语言、嵌入式中几个非常实用的宏技巧
宏打印函数
在我们的嵌入式开发中,使用printf打印一些信息是一种常用的调试手段。但是,在打印的信息量比较多的时候,就比较难知道哪些信息在哪个函数里进行打印。
特别是对于异常情况的打印,我们需要快速定位到异常情况的位置。
这时候我们可以使用宏定义来封装一个宏打印函数,这个宏打印函数可以显示打印信息所在的文件、行数、函数名等信息。如:
左右滑动查看全部代码>>>
#define DBG_PRINTF(fmt, args...) \
{\printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\printf(fmt, ##args);\
}
使用范例:
可见,使用方法与printf的使用方法一样,而且每条打印语句开头都会打印调试信息所在的文件名、行号、函数名信息,方便我们查找一些调试信息。
其中,__FILE__
、__LINE__
、__FUNCTION__
这三个宏是编译器内置宏定义,分别代表调试信息所在文件、行号、函数。
除此之外,常用的宏还有:__DATE__
、__TIME__
,分别代表当前的编译日期与时间。如:
DBG_PRINTF("Compile Time: %s %s\n", __DATE__, __TIME__);
第二条printf中的##符号是为了处理args不代表任何参数的情况。如:
DBG_PRINTF("Hello world");
当不加##符号是,以上宏的第二条语句被拓展为:
printf("Hello world\n", );
可见,多出了一个逗号,这个逗号是多余的。
加上##符号后,以上宏的第二条语句被拓展为:
printf("Hello world\n");
这才是我们想要的结果。其实这些结果我们通过查看预处理文件可以清晰的知道:
最后需要注意的是,这个DBG_PRINTF还是与printf不一样的。DBG_PRINTF宏是两条语句的组合,无返回值;而printf的原型是:
int printf (const char *__format, ...)
但是我们一般都很少使用printf的返回值,所以DBG_PRINTF的用法与printf函数基本一致。
打印调试宏开关
通常情况下,一些打印调试信息只是在我们调试阶段需要的,在程序发布阶段是不需要的。
所以,为了避免打印调试信息带来的资源开销,我们可以把这些打印调试语句给注释掉。
一种方法是逐句进行注释,这是一种比较低效的方法。比较高效的方法就是添加调试宏开关,利用条件编译来选择打印/不打印调试信息。
比如我们可以把上面的代码改造为:
#define DEBUG 1 #if DEBUG#define DBG_PRINTF(fmt, args...) \{\printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\printf(fmt, ##args);\}
#else#define DBG_PRINTF(fmt, args...)
#endif
根据DEBUG宏的值来选择对应的打印宏函数。当DEBUG的值为1时启动相关的打印调试语句,DEBUG的值为0时则关闭打印调试语句。
这样我们就可以很方便的通过设置DEBUG宏的值来启动与关闭我们整个工程的DBG_PRINTF打印调试信息。
do{}while(0)
其实,上面我们封装的打印宏DBG_PRINTF还有一点缺陷,比如我们与if、else使用的时候,会有这样的一种使用情况:
此时会报语法错误。为什么呢?
同样的,我们可以先来看一下我们的demo代码预处理过后,相应的宏代码会被转换为什么。如:
这里我们可以看到,我们的if、else结构代码被替换为如下形式:
if(c)
{ /* ....... */ };
else
{ /* ....... */ };
显然,出现了语法错误。if之后的大括号之后不能加分号,这里的分号其实可以看做一条空语句,这个空语句会把if与else给分隔开来,导致else不能正确匹配到if,导致语法错误。
为了解决这个问题,有几种方法。第一种方法是:把分号去掉。代码变成:
第二种方法是:在if之后使用DBG_PRINTF打印调试时总是加{}。代码变成:
以上两种方法都可以正常编译、运行了。
但是,我们C语言中,每条语句往往以分号结尾;并且,总有些人习惯在if判断之后只有一条语句的情况下不加大括号;而且我们创建的DBG_PRINTF宏函数的目的就是为了对标printf函数,printf函数的使用加分号在任何地方的使用都是没有问题的。
基于这几个原因,我们有必要再对我们的DBG_PRINTF宏函数进行一个改造。
下面引入do{}while(0)来对我们的DBG_PRINTF进行一个简单的改造。改造后的DBG_PRINTF宏函数如下:
#define DBG_PRINTF(fmt, args...) \
do\
{\printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\printf(fmt, ##args);\
}while(0)
这里的do...while循环的循环体只执行一次,与不加循环是效果一样。并且,可以避免了上面的问题。预处理文件:
我们的宏函数实体中,while(0)后面不加分号,在实际调用时补上分号,既符合了C语言语句分号结尾的习惯,也符合了do...while的语法规则。
使用do{}while(0)来封装宏函数可能会让很多初学者看着不习惯,但必须承认的是,这确确实实是一种很常用的方法。
在STM32的HAL库中搜索while(0):
在Linux源码中搜索while(0):
可见,在实际应用中,do{}while(0)用的很多。
#运算符与##运算符
这两个运算符之前也有分享过,这里顺便也提一下。
#号
作为一个预处理运算符
,可以把记号转换成字符串。
例如,如果A是一个宏形参,那么#A就是转换为字符串"A"的形参名。这个过程称为字符串化(stringizing)
。以下程序演示这个过程:
##运算符可以把两个记号组合成一个记号。以下程序演示这个过程:
这个运算符用得很多。如:
最后
以上就是本次的分享。如有错误,欢迎指出!谢谢
本篇笔记会同步至我的个人博客:https://www.lizhengnian.cn/中,欢迎来访。
原创不易,期待您的在看、分享~
===========
PS:想加入技术群的同学,加了我好友后,就给我发「篮球的大肚子」这句话,有可能机器人打瞌睡,可以多发几次,不要发与技术无关的消息或者推广。
如果想获取学习资料,就在公众号后台回复「1024」,足够多的学习资料可以让你学习。
C语言、嵌入式中几个非常实用的宏技巧相关推荐
- .net 宏定义_C语言、嵌入式中一些实用的宏技巧
宏打印函数 在我们的嵌入式开发中,使用printf打印一些信息是一种常用的调试手段.但是,在打印的信息量比较多的时候,就比较难知道哪些信息在哪个函数里进行打印. 特别是对于异常情况的打印,我们需要快速 ...
- UI界面设计中的5个实用版面排版技巧
UI 界面中,排版设计同样重要.如何将至关重要的信息,有效地传递给用户,这是界面本身的职责.实际上, 排版本身并不是选择字体,也不是制作字体和布局,它是塑造文本的呈现形式,达到最佳体验的过程. 安全. ...
- 制作ppt中几个比较实用好玩的技巧
1.合并形状. 第一步 ,插入两个图形,(图形摆放位置不同,产生的效果也不同)同时选中两个图形. 第二步,点击格式选项,选择合并形状,有多种合并方式.可以多尝试几次. 第三步,话不多说,直接上图: 其 ...
- Flex 中 12 个简单实用的小技巧
1. 复制内容到剪贴板 System.setClipboard(strContent); 2. 复制一个 ArrayCollection //dummy solution( well, it work ...
- C语言嵌入式系统编程修炼(经典中的经典)
C语言嵌入式系统编程修炼 http://blog.chinaunix.net/u/25764/showart_326589.html转载自这里,真是太经典了. C语言嵌入式系统编程修炼 ...
- c语言课程存在的问题,计算机C语言教学中存在的问题与对策
丁红 [摘要]随着信息技术的不断发展,计算机的应用范围越来越广,人们的生产.生活.学习.工作等诸多方面,都有计算机技术的涉足.为了持续为社会输送更多的计算机人才储备力量,计算机教育的持续推进已成必然. ...
- 嵌入式C语言自我修养 04:Linux 内核第一宏:container_of
4.1 typeof 关键字 ANSI C 定义了 sizeof 关键字,用来获取一个变量或数据类型在内存中所占的存储字节数.GNU C 扩展了一个关键字 typeof,用来获取一个变量或表达式的类型 ...
- 嵌入式C语言自我修养 (04):Linux 内核第一宏:container_of
4.1 typeof 关键字 ANSI C 定义了 sizeof 关键字,用来获取一个变量或数据类型在内存中所占的存储字节数.GNU C 扩展了一个关键字 typeof,用来获取一个变量或表达式的类型 ...
- C语言嵌入式系统编程修炼之道——性能优化篇
C语言嵌入式系统编程修炼之道--性能优化篇 作者:宋宝华 e-mail:[email]21cnbao@21cn.com[/email] 1.使用宏定义 在C语言中,宏是产生内嵌代码的唯一方法.对于嵌 ...
最新文章
- 2022-2028年中国水性密封胶行业市场调查研究及未来趋势预测报告
- python线程信号量semaphore
- android loadlibrary 更改libPath 路径,指定路径加载.so
- mongodb studio 3t 破解无限使用脚本
- mysql pt_mysql之pt工具之pt-fifo-split用法介绍
- 三轴加速度传感器和六轴惯性传感器_美泰产品推介MSV3100A三轴加速度传感器
- 【编译原理】FIRSTVT和LASTVT求法
- 洛谷p2704 炮兵阵地
- 算法—递归实现 C(m,n)
- LeetCode 545. 二叉树的边界(前序+后序)*
- 《数据分析思维手册》和《数据分析师的职场真相》全集整理好啦,下载保存!...
- CF probabilities 自制题单
- LTCC带通滤波器设计
- 护照扫描仪出入境海关运用SDK
- 感觉现在的技术圈越来越像娱乐圈了
- 计算机常用键的作用,键盘功能键大全2017 电脑键盘常用按键功能详解
- 单片机 c语言 可控硅,单片机控制可控硅电路
- 可以帮助提升程序员高效工作效率的常用小工具推荐
- java md5加密 jar包_BeanShell调用自己写的jar包进行MD5加密
- TV服务器的安装维护和调试,广电机顶盒安装调试教程及系统设置密码
热门文章
- Oracle Class4. 数据库对象(同义词,序列,视图,索引,簇)
- 庆祝51CTO六周年:资源牛人有奖比拼,生日当天疯狂送豆!(已结束)
- Backtrader交易基础2
- 在微型计算机中 如果电源突然中断,微型计算机在工作中电源突然中断,则其中的信息全部丢失,再次通电后也不能恢复的..._考试资料网...
- JAVA命令符找不到符号_[转]Java命令行编译文件时出现的错误,找不到符号或软件包不存在等...
- 【解决】jupyter在deepin安装上的坑
- 手写数字识别中多元分类原理_广告行业中那些趣事系列:从理论到实战BERT知识蒸馏...
- CATia对计算机配置要求,【2人回答】求CATIA对电脑的详细配置要求-3D溜溜网
- java ee 指南 pdf_Java EE 7权威指南:卷1(原书第5版) 中文pdf
- 网络层核心:路由和路由生成算法