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

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

3天内不再提示

嵌入式开发中实用的宏打印函数

FPGA之家 来源:嵌入式大杂烩 作者:ZhengNL 2021-09-16 10:05 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

宏打印函数在我们的嵌入式开发中,使用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

”, __DATE__, __TIME__);

第二条printf中的##符号是为了处理args不代表任何参数的情况。如:

DBG_PRINTF(“Hello world”);

当不加##符号是,以上宏的第二条语句被拓展为:

printf(“Hello world

”, );

可见,多出了一个逗号,这个逗号是多余的。

加上##符号后,以上宏的第二条语句被拓展为:

printf(“Hello world

”);

这才是我们想要的结果。其实这些结果我们通过查看预处理文件可以清晰的知道:

最后需要注意的是,这个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使用的时候,会有这样的一种使用情况:

a14a24d0-15f8-11ec-8fb8-12bb97331649.png

此时会报语法错误。为什么呢?

同样的,我们可以先来看一下我们的demo代码预处理过后,相应的宏代码会被转换为什么。如:

a158cc60-15f8-11ec-8fb8-12bb97331649.png

这里我们可以看到,我们的if、else结构代码被替换为如下形式:

if(c)

{ /* 。..。..。 */ };

else

{ /* 。..。..。 */ };

显然,出现了语法错误。if之后的大括号之后不能加分号,这里的分号其实可以看做一条空语句,这个空语句会把if与else给分隔开来,导致else不能正确匹配到if,导致语法错误。

为了解决这个问题,有几种方法。第一种方法是:把分号去掉。代码变成:

a16a0f5c-15f8-11ec-8fb8-12bb97331649.png

第二种方法是:在if之后使用DBG_PRINTF打印调试时总是加{}。代码变成:

a17d3866-15f8-11ec-8fb8-12bb97331649.png

以上两种方法都可以正常编译、运行了。

但是,我们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循环的循环体只执行一次,与不加循环是效果一样。并且,可以避免了上面的问题。预处理文件:

a188ca32-15f8-11ec-8fb8-12bb97331649.png

我们的宏函数实体中,while(0)后面不加分号,在实际调用时补上分号,既符合了C语言语句分号结尾的习惯,也符合了do.。.while的语法规则。

使用do{}while(0)来封装宏函数可能会让很多初学者看着不习惯,但必须承认的是,这确确实实是一种很常用的方法。

STM32的HAL库中搜索while(0):

a193dd8c-15f8-11ec-8fb8-12bb97331649.png

a1a28710-15f8-11ec-8fb8-12bb97331649.png

Linux源码中搜索while(0):

a1b3e5d2-15f8-11ec-8fb8-12bb97331649.png

可见,在实际应用中,do{}while(0)用的很多。

#运算符与##运算符这两个运算符之前也有分享过,这里顺便也提一下。

#号作为一个预处理运算符,可以把记号转换成字符串。

例如,如果A是一个宏形参,那么#A就是转换为字符串“A”的形参名。这个过程称为字符串化(stringizing)。以下程序演示这个过程:

##运算符可以把两个记号组合成一个记号。以下程序演示这个过程:

这个运算符用得很多。如:

责任编辑:haq

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

    关注

    5209

    文章

    20655

    浏览量

    336980
  • 代码
    +关注

    关注

    30

    文章

    4976

    浏览量

    74376

原文标题:嵌入式中几个非常实用的宏技巧~

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    低代码可视化,破解嵌入式开发高门槛难题

    在工业控制、智能硬件、物联网快速发展的今天,嵌入式开发作为核心支撑技术,其高门槛却成为行业发展的绊脚石。专业编码要求高、新人上手慢、项目迭代滞后等问题,制约着人才储备与技术普及
    的头像 发表于 03-25 14:13 197次阅读
    低代码可视化,破解<b class='flag-5'>嵌入式开发</b>高门槛难题

    RT-Thread Vector软件包:嵌入式开发的动态数组容器 | 技术集结

    RT-Thread Vector软件包:嵌入式开发的动态数组容器 | 技术集结
    的头像 发表于 01-25 09:33 5635次阅读
    RT-Thread Vector软件包:<b class='flag-5'>嵌入式开发</b>的动态数组容器 | 技术集结

    嵌入式开发常用函数速查表

    嵌入式开发,掌握常用函数的用法可以大大提高开发效率。无论是单片机初学者还是有一定经验的工程师,熟悉函数库和调用方式都是必备技能。今天,我
    的头像 发表于 01-19 09:06 465次阅读
    <b class='flag-5'>嵌入式开发</b>常用<b class='flag-5'>函数</b>速查表

    什么是嵌入式应用开发

    概述 所谓的嵌入式应用开发就是在嵌入式操作系统下进行开发、软硬件综合开发嵌入式应用
    发表于 01-12 16:13

    性能与性价比兼得!迅为RK3562开发板开启嵌入式开发新选择

    性能与性价比兼得!迅为RK3562开发板开启嵌入式开发新选择
    的头像 发表于 12-23 16:10 1705次阅读
    性能与性价比兼得!迅为RK3562<b class='flag-5'>开发</b>板开启<b class='flag-5'>嵌入式开发</b>新选择

    一个面向单片机、事件驱动的嵌入式开发平台介绍

    EventOS,是一个面向单片机、事件驱动的嵌入式开发平台。它主要有两大技术特色:一是事件驱动,二是超轻量。EventOS以及其母项目EventOS,目标是开发一个企业级的嵌入式开发平台,以事件总线
    发表于 12-05 06:26

    分享一个嵌入式开发学习路线

    法,这些语法在嵌入式开发至关重要。 2. 技能进阶期(2-3个月) 从51单片机过渡到主流的ARM Cortex-M系列(嵌入式就业核心平台),学会使用STM32单片机,掌握嵌入式开发
    发表于 12-04 11:01

    嵌入式开发的关键点介绍

    嵌入式开发在现代科技扮演着非常重要的角色。随着物联网的发展,嵌入式系统的需求也越来越大。嵌入式开发不仅需要开发人员具备深入的硬件知识和编程
    发表于 11-13 08:12

    GT2440嵌入式开发系统用户手册

    电子发烧友网站提供《GT2440嵌入式开发系统用户手册.pdf》资料免费下载
    发表于 10-28 17:05 2次下载

    恩智浦如何攻克两大嵌入式开发难题

    在我们的身边,嵌入式系统无处不在。而随着技术的进步和应用的拓展,今天的嵌入式开发也面临着诸多新课题、新挑战。
    的头像 发表于 10-27 09:28 2027次阅读

    新一代嵌入式开发平台 AMD嵌入式软件和工具2025.1版现已推出

    AMD 2025.1 版嵌入式软件和工具是面向新一代嵌入式系统开发而打造的综合平台,全面加速概念构想到部署落地。 2025.1 版嵌入式软件和工具的新 增功能 AMD
    的头像 发表于 08-20 09:15 4208次阅读

    嵌入式开发就业还有前景吗?

    嵌入式的发展前景如何呢?随着科技的飞速发展,嵌入式开发在各个领域的应用越来越广泛,因此嵌入式开发的就业前景也备受关注。以下将从智能家居、医疗设备、工业控制、汽车电子、航空航天等方面,探讨嵌入式
    发表于 07-16 10:47

    明远智睿SSD2351开发板:嵌入式开发领域的新型新星

    在物联网、智能家居、智能网关、视频监控等新兴技术领域迅猛发展的今天,嵌入式开发作为这些创新背后的核心驱动力,其重要性日益凸显。而在嵌入式开发的众多环节开发板的选择无疑是决定项目成败
    的头像 发表于 07-01 16:47 892次阅读

    嵌入式开发,如何选择适合的系统?

    嵌入式ARM开发,面对多种操作系统的选择,如何做出最适合项目的决策?本文将为您梳理常见系统的特性,帮助您快速了解它们的优缺点,以便更好地选择适合的系统。引言在嵌入式ARM
    的头像 发表于 05-20 11:32 1296次阅读
    <b class='flag-5'>嵌入式开发</b>,如何选择适合的系统?

    嵌入式开发入门指南:从零开始学习嵌入式

    随着物联网、智能硬件的发展,嵌入式开发成为热门技能之一。以下将为初学者提供一份详细的嵌入式开发入门指南,涵盖学习路径、必备工具、推荐资源等内容。 1. 嵌入式系统的定义与应用嵌入式系统
    发表于 05-15 09:29