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」,足够多的学习资料可以让你学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

canvas 多次画图效果_canvas练习之终极的奔跑小人

这次做一个终极的练习&#xff0c;先看一下最后的效果。一个不停奔跑的小人&#xff0c;点击鼠标后会让他跑到目的地&#xff0c;并且呈现不同的角度。下面来看一下如何一步步来实现它的。准备网上下载了一张图片&#xff0c;其中包含了小人面向不同角度奔跑的各个分解动作。新…

C语言的爱你永不悔

#前言今天是520节&#xff0c;昨天晚上睡觉的时候&#xff0c;我家地瓜一直说520,520&#xff0c;我不知道要干啥&#xff0c;我就说&#xff0c;要睡就睡&#xff0c;不睡就起来。今天我看了朋友圈&#xff0c;我才知道原来520就是「我爱你」的意思。今天在朋友圈看到的一个小…

服务网关zuul之二:过滤器--请求过滤执行过程(源码分析)

Zuul的核心是一系列的过滤器&#xff0c;这些过滤器可以完成以下功能&#xff1a; 身份认证与安全&#xff1a;识别每个资源的验证要求&#xff0c;并拒绝那些与要求不符的请求。审查与监控&#xff1a;在边缘位置追踪有意义的数据和统计结果&#xff0c;从而带来精确的生成视图…

攻防比赛_2020年度泉州市大学生网络安全攻防比赛在黎明职业大学圆满落幕

10月16日下午&#xff0c;作为2020年国家网络安全宣传周泉州市系列活动之一&#xff0c;“泉州市大学生网络安全攻防比赛”在黎明职业大学智慧教学中心成功举办并圆满落幕。此次比赛由泉州市互联网信息办公室、泉州市教育局主办&#xff0c;黎明职业大学和泉州市网络与信息安全…

gitlab 如何关闭force push

把不允许force push的分支设置成保护分支&#xff0c;Push的时候就会提示不能force psuh。

camera中文版软件 ip_ip camera网络摄像机

IP Camera Viewer是一个方便可靠能够通过IP地址监控多台摄像头的软件。 需要的朋友们赶紧下载试试吧&#xff01;你可以在几分钟之内设置一个免费的IP摄像监控系统&#xff0c; 保持在家里&#xff0c;办公室&#xff0c;停车场或任何地方&#xff0c;你都可以监控。同时查看多…

Ubuntu16.04通过GPT挂载硬盘

一般而言&#xff0c;服务器上挂载的硬盘都是比较大的&#xff0c;传统的对硬盘进行分区需要在终端敲sudo fdisk进行操作&#xff0c;但是&#xff0c; 当挂载的硬盘的容量大于2T的时候&#xff0c;是无法通过sudo fdisk进行挂载的&#xff0c;这个时候必须要进行GPT进行挂载&a…

Mplayer后台播放没有声音

昨天的文章&#xff0c;我觉得虽然不是很复杂&#xff0c;但是我自认为很多人应该是不懂的&#xff0c;不过好像阅读量不是很好&#xff0c;今天转发我师弟的一篇文章&#xff0c;我觉得这个也是大家没有注意到的。Linux 下的0 1 2特殊文件描述符~一、平台Ubuntu16.04(64位)二、…

python输出程序运行时间_叨叨 Python 性能优化工具

虽然Python是一个”慢慢的“语言&#xff0c;但是不代表我们对性能没有任何的追求&#xff0c;在程序运行过程中&#xff0c;如果发现程序运行时间太长或者内存占用过大&#xff0c;免不了需要对程序的执行过程进行一些监测&#xff0c;找到有问题的地方&#xff0c;进行优化。…

链接学习之obj文件探索

Windows的gcc环境&#xff0c;往官网http://sourceforge.net/project/showfiles.php?group_id2435 下载MinGW&#xff0c;安装&#xff0c;安装完毕后按照包 配置环境变量 a.在PATH的值中加入"C:\Program Files\MinGWStudio\MinGW\bin"。这是寻找gcc编译器的路径。…

http 请求默认时间_JMeter接口测试之HTTP请求默认值

不管是在UI级别的自动化测试还是在接口级别的自动化测试中&#xff0c;对公共数据数据的分离都是一种趋势&#xff0c;或者某种程度来说&#xff0c;这是自动化测试中必须要掌握的一种能力&#xff0c;是基本技能。这些公共数据就包含了测试地址&#xff0c;以及登录的账号密码…

有意思的select~

前言最近在写一个小程序&#xff0c;也就是简单的系统调用&#xff0c;但是神奇的是&#xff0c;我用的这个系统调用刚好就阻塞了。如果你也写过应用程序&#xff0c;肯定也会遇到过这样的问题。后来&#xff0c;发现了select这个好东西&#xff0c;可以用来监听文件描述。sele…

cesium 页面截图_Cesium开发入门篇 | 02开发环境搭建及第一个示例

开发环境准备利用Cesium API进行二次开发属于Web前端开发范畴&#xff0c;目前比较火的Web三剑客包括React、Vue、AngularJS&#xff0c;每个js库的详细介绍可转至官网查看&#xff0c;在此不做详细介绍。本次开发环境是基于Vue搭建的&#xff0c;需要安装(部署)的软件主要包括…

一个单片机ADC的挖坑填坑之旅

[导读] 本文来解析一个盆友在使用STM32采集电池电压踩过的坑。以STM32F4 的ADC属于逐次逼近SAR 型ADC为例进行分析&#xff0c;参考STM32F405xx Datasheet&#xff0c;对于如何编写ADC程序就不做描述了。先描述一下坑 采集电池电压&#xff0c;利用两个电阻将电池电压分压&…

of_property_read_string 剖析~

前言今天在一个群里面看到的一个朋友提交&#xff0c;说of_property_read_string 这个函数有两个定义&#xff0c;到底是用了哪个呢&#xff1f;所以这篇文章就说下这个函数。函数引用的头文件引用的头文件位置在\kernel-4.4\include\linux\of.h其中一个是extern int of_proper…

CPU频率和核心

设置CPU的核心数在/sys/devices/system/cpu目录下可以看到你的CPU有几个核心&#xff0c;如果是四核&#xff0c;就是cpu0&#xff0c;cpu1&#xff0c;cpu2,cpu3 4个文件夹。cpu0 常开。进一个其他文件夹&#xff0c;比如cpu1&#xff0c;里面有个online文件用cat命令查看该文…

关于“进程”与“线程”的最通俗解析

来源&#xff1a;电子工程专辑进程&#xff08;process&#xff09;和线程&#xff08;thread&#xff09;是操作系统的基本概念&#xff0c;但是它们比较抽象&#xff0c;不容易掌握。最近&#xff0c;我读到一篇材料&#xff0c;发现有一个很好的类比&#xff0c;可以把它们解…

要想选到音质好的耳机,你应该需要知道这些~

最近在一个音频公司调试我们设备的音频&#xff0c;从这次调试中&#xff0c;有所收获&#xff0c;希望这次的吹牛大家看完后&#xff0c;以后去买音频产品&#xff0c;可以分辨什么是好的&#xff0c;什么是不好的。有些产品硬件没有问题&#xff0c;但是产品经理因为个人喜好…

Fantasia (Tarjan+树形DP)

Time Limit: 1000 ms Memory Limit: 256 MB Description 给定一张N个点、M条边的无向图 $G$ 。每个点有个权值Wi。 我们定义 $G_i$ 为图 $G$ 中删除第 $i$ 号顶点后的图。我们想计算 $G_1, G_2, ..., G_n$ 这N张图的权值。 对于任意一张图 $G$ &#xff0c;它的权值是这样定义…

买书这件事

知识这种东西&#xff0c;你只有不断的补充才不会觉得匮乏&#xff0c;我每年都会买点书&#xff0c;我喜欢买书&#xff0c;但是却不看书&#xff0c;很多书籍我都是当成工具书来用。我记得在2015年的时候&#xff0c;我需要自己写专利&#xff0c;但是我对写专利这个事情一窍…