什么是断言函数呢?断言函数在调试中的应用

电子说

1.2w人已加入

描述

这一次我们继续讲调试方法。调试是排查程序Bug的有效方法,同时也对嵌入式软件设计的可靠性、稳定性而言至关重要。之前讲的调试方法能够打印出变量值、系统状态,或用互动的方式去调试程序,都不能动态的在系统运行时由程序判断变量、参数是否出错。

而我们今天要讲的断言(assert)函数则能做到在运行时判断参数是否超出预设值、状态是否出错,然后打印出出错数据所在的源文件和行号。

那么,什么是断言函数呢?百度百科给的定义是:“断言(assertion)是一种在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果——当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。“

接下来,我们继续采用上一次实时跟踪调试的例子,加入断言函数对运行过程的参数进行判断,看看断言函数如何应用,有什么效果。

1. CubeMX设置

我们可以在CubeMX中打开例子工程中的.ioc文件,按下图进行设置。

C语言

除此之外,可以直接在CubeIDE的工程属性里定义一个宏USE_FULL_ASSERT,也可以在工程任意头文件中定义这个宏,效果是一样的。其实采用CubeMX配置之后,就是在工程的stm2f7xx_hal_conf.h头文件中定义了这个宏。

2. 修改代码

当定义了宏USE_FULL_ASSERT之后,assert_failed函数就能参与编译了,这个函数在main.c的最下边。这个函数的代码如下:

void assert_failed(uint8_t *file, uint32_t line)
{
printf("Wrong parameters value: file %s on line %drn", file, (uint16_t)line);
}

断言失败的话则会执行这个函数,利用printf打印一条消息,这里我们用的是CubeIDE的ITM模块向外打印,打印的消息里包含断言失败语句所在的源文件及行数。

要注意的是,参数line本来是无符号长整形,printf函数用%d对应长整形的话会给警告,所以做了一个强制类型转换,变为无符号短整型。我想应该不会有一个源文件超过65535行吧,那是要挨打的。

接下来在main.h里定义一个宏IS_PARA_COUNTER_OK,当然名字可以自己任意取。

#define IS_PARA_COUNTER_OK(para) (para < 5)

这个宏的其实是个表达式,用以对para参数的值进行判断,这里假设para的值小于5是正常的。为了防止出错,表达式用小括号括起来了。

在main函数while循环开始的地方,我们加上一条语句,用来对我们设置的一个用来计数的变量counter进行参数断言。

assert_param(IS_PARA_COUNTER_OK(counter));

其中,assert_param是在stm2f7xx_hal_conf.h中定义的一个宏。

#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *) FILE , LINE ))

意思是当expr表达式的值为真的时候,不执行任何操作,为假时,断言失败,执行assert_failed函数,并向该函数传递断言失败语句所在的源文件和行。__FILE__和__LINE__都是C语言定义的宏,分别代表当前源文件和所在行。

我们在main函数中写的断言语句可以完全展开如下:

(((counter < 5)) ? (void)0U : assert_failed((uint8_t *)"D:workspaceSTM32F7example2_ITMCoreSrcmain.c", 101))

是的,这条语句位于main.c的101行。

3. 调试结果

代码修改好后,连接好开发板,构建工程,进入调试模式并开始运行,我们可以在SWV ITM Data Console窗口看到如下信息。

C语言

这里要说明一下,代码里counter值是在打印之后加1的,也就是说在打印出4之后,其值已经变为5,导致参数断言出错,打印出预设消息。另外我们也可以在assert_failed函数里加入一个死循环,断言失败后程序就不会继续往下执行了。

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

全部0条评论

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

×
20
完善资料,
赚取积分