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

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

3天内不再提示

防御式编程之断言assert的使用

CHANBAEK 来源:明解嵌入式 作者:Sharemaker001 2023-04-19 11:35 次阅读

防御式编程的重点就是需要防御一些程序未曾预料的错误,这是一种提高软件质量的辅助性方法,断言assert就用于防御式编程,编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。 使用断言是为了验证预期的结果——当程序执行到断言的位置时,对应的断言应该为真; 若断言不为真时,程序会终止执行,并给出错误信息。 可以在任何时候启用和禁用断言验证,因此可以在程序调试时启用断言而在程序发布时禁用断言。 同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。

1、原型函数

在大部分编译器下,assert() 是一个宏; 在少数的编译器下,assert() 就是一个函数。 我们不需要关心这些差异,可以只把 assert()当作函数使用即可。 即:

void assert(int expression)

在程序运行时它会计算括号内的表达式,如果 expression为非0说明其值为真,assert()不执行任何动作,程序继续执行后面的语句; 如果 expression为0说明其值为假,assert()将会报告错误,并终止程序的执行,值得了解的是,程序终止是调用abort()函数,这个函数功能就是终止程序执行,直接从调用的地方跳出,abort()函数也是标准库函数,在

2、详细释义

assert() 在c标准库中的

#ifdef NDEBUG
#define assert(e) ((void)0)
#else
#define assert(e)  
    ((void) ((e) ? ((void)0) : __assert (#e, __FILE__, __LINE__)))
#endif

可以看到在定义了NDEBUG时,assert()无效,只有在未定义NDEBUG时,assert()才实现具体的函数功能。 NDEBUG是“No Debug”的意思,也即“非调试”。 程序一般分为Debug版本和Release版本,Debug版本是程序员在测试代码期间使用的编译版本,Release版本是将程序提供给用户时使用的发布版本,一般来说断言assert()是仅在Debug版本起作用的宏。 在发布版本时,我们不应该再依赖assert()宏,因为程序一旦出错,assert()会抛出一段用户看不懂的提示信息,并毫无预警地终止程序执行,这样会严重影响软件的用户体验,所以在发布模式下应该让assert()失效,另外在程序中频繁的调用assert()会影响程序的性能,增加额外的开销。 因此可以在

#define NDEBUG  //定义NDEBUG  
#ifdef NDEBUG
#define assert(e) ((void)0)
#else
#define assert(e)  
    ((void) ((e) ? ((void)0) : __assert (#e, __FILE__, __LINE__)))
#endif
  • 定义NDBUG时:

当定义了NDEBUG之后,assert()执行的具体函数就变成了 ((void)0),这表示啥也不干了,宏里面这样用的目的是防止该宏被用作右值,因为void类型不能用作右值。 所以当在头文件中定义了NDEBUG之后,assert()的检测功能就自动失效了。

  • 未定义NDBUG时:

可以看到assert()执行实际上是通过三目运算符来判断表达式e的真假,执行相应的处理。 当表达式e为真时,执行(void)0,即什么也不执行,程序继续运行; 当表达式e为假时,那么它会打印出来assert的内容、当前的文件名、当前行号,接着终止程序执行。

3、用法举例

在未定义NDBUG时,assert()功能生效的情况下,来看一个简单的assert()使用的例子:

#include 
#include 
void main()
{
    int i = 8;
    assert(i > 0);
    printf("i = %d\\n", i);
    i = -8;
    assert(i > 0);
    printf("i = %d\\n", i);
}

可以看出在程序中使用assert(i > 0)来判断; 当 i > 0 时,assert的判断表达式为真,assert不生效; 当 i < 0 时,assert的判断表达式为假,assert生效。

在程序第5行 i = 8,执行完assert后,程序将执行后续的printf打印出 i 的值; 而在第8行 i = -8,执行完assert后,程序将终止,不会执行后续的printf。

4、使用注意事项

使用assert的核心原则是:用于处理绝不应该发生的情况,这就是为什么应该在程序Debug版本中使用,这是为了将主观上不应该发生的错误在Debug版本中就应该解决掉,从而在程序Release版本时不会产生这种不应该发生的类型的错误。

  • 和if的区别

assert用函数来判断是否满足表达式条件后终止程序,在Debug版本中用assert来判断程序的合法性,定位不允许发生的错误,那么什么是不应该发生的错误,例如像下面这种除0操作,主观上就不应该发生,就是就要在Debug版本中检查排除掉这种错误,以免影响后续程序的执行。

#include 
#include 
void fun(int a, int b)
{
    assert(b != 0);
    int i = a / b;
}

if是一个关键字,一般用于根据条件来判断逻辑的正确性,即是否根据条件对应执行,Debug和Release版本中都可以使用,例如下面用if的时候,就允许这些判断条件是正常发生的,是合理的,需要根据发生的条件执行对应的逻辑,程序可以往下执行。

#include 
#include 
void fun(int a, int b)
{
   if(a > 0)
       ...
   else if(a < 0)
       ...
   else
       ...
}

因此在使用前,可以先判断下,如果逻辑不允许发生,那么就使用assert在Debug阶段将问题解决掉; 如果逻辑允许的,那么就使用if,当然也可以用if判断后进行条件的return操作,来杜绝不允许逻辑,本质是防止错误的逻辑影响后续程序的执行。 例如上述的用来判断除0操作的例子也可以用if:

#include 
#include 
void fun(int a, int b)
{
    if(0 == b)
        return;
    int i = a / b;
}
  • 用于判断函数的入参

一般assert可以用于判断函数入参的合法性,比如入参值是否符合,指针是否为空:

#include 
#include 
void fun1(int a)
{
    assert(a > 0);
    ...
}
void fun2(int *p)
{
    assert(p != NULL);
    ...
}
  • 不要使用影响正常逻辑的判断条件语句

assert的判断条件语句一定是确定的,在Debug版本中使用的排除掉错误的条件逻辑,不要影响到Release版本时的正常逻辑。 例如下面的例子,在Debug版本时,i++到>=100时,assert生效,程序终止; 但是到了Release版本,由于要增加NDEBUG宏,assert()无效。 assert(i++ < 100)就变成了空操作(void)0;由于没有i++语句执行,那么while成了死循环。

#include 
#include 
void main()
{
    int i = 0;
    while(i <= 110)
    {
        assert(i++ < 100);
        printf("i = %d\\n",i);
    }
}
  • 不要用多个判断条件语句

一般一个assert只用一个判断语句来实现,如果在一个assert中使用多条判断语句,当错误发生时,会不知道是哪个条件语句出现错误,错误表现的就不直观。

#include 
#include 
void fun1(int a, int b) //错误使用
{
    assert(a > 0 && b > 5);
    ...
}
void fun2(int a, int b) //正确使用
{
    assert(a > 0);
    assert(b > 5);
    ...
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3614

    浏览量

    93698
  • 函数
    +关注

    关注

    3

    文章

    4329

    浏览量

    62579
  • 编译器
    +关注

    关注

    1

    文章

    1624

    浏览量

    49113
  • void
    +关注

    关注

    0

    文章

    23

    浏览量

    9868
  • ASSERT
    +关注

    关注

    0

    文章

    17

    浏览量

    7239
收藏 人收藏

    评论

    相关推荐

    C语言中assert的使用

    assert意思是断言,常用在程序的DEBUG版本中。
    发表于 07-21 14:51 843次阅读

    解析C语言断言函数的使用

    对于断言,相信大家都不陌生,大多数编程语言也都有断言这一特性。简单地讲,断言就是对某种假设条件进行检查。 在 C 语言中,断言被定义为宏的形
    发表于 08-08 09:51 461次阅读
    解析C语言<b class='flag-5'>断言</b>函数的使用

    何为断言断言的作用有哪些?断言的种类 断言层次结构

    断言主要用来检查仿真过程中存在的时序问题,如果存在异常情况,断言会报警。一般在数字电路设计中都要加入断言断言占整个设计的比例应不少于30%。
    的头像 发表于 08-28 11:16 8429次阅读
    何为<b class='flag-5'>断言</b>?<b class='flag-5'>断言</b>的作用有哪些?<b class='flag-5'>断言</b>的种类 <b class='flag-5'>断言</b>层次结构

    C语言assert(断言)简介

    assert的功能,条件为真,程序继续执行;如果断言为假(false),则程序终止。
    的头像 发表于 11-17 16:33 1155次阅读
    C语言<b class='flag-5'>assert</b>(<b class='flag-5'>断言</b>)简介

    如何在if和assert中做选择

    的功能。以前我也是这么想的,但是,现在我不这么认为。 二、assert 断言刚才,我问了下旁边的一位工作 5 年多的嵌入开发者:if 和 assert 如何选择?他说:
    发表于 04-08 06:13

    请问HAL函数对Handle有效性的检查为什么不是用assert_param断言

    )); ...... } 以HAL_SPI_Init为例,hspi参数的检查并没有使用assert_param断言宏,如果是我实现的话,我会用assert_param(hspi != NULL)实现。一般
    发表于 05-08 07:00

    断言ASSERT)的用法

    的了解,assert()的用法像是一种“契约编程”,在我的理解中,其表达的意思就是,程序在我的假设条件下,能够正常良好的运作,其实就相当于一个if语句:if(假设成立){程序正常运行;}else
    发表于 08-23 09:33

    如何在XC8中使用断言的?

    大家好,我正在尝试使用XC8中的断言,但是当我使用“*”时,“断言h”空格main(空隙){BOOL X=0;断言(x= 1);而(1){}}我的程序停止,并且在控制台中不显示任何MsAGAGEM
    发表于 03-26 10:58

    嵌入开发中防御编程的要求

    嵌入产品的可靠性自然与硬件密不可分,但在硬件确定、并且没有第三方测试的前提下,使用防御编程思想写出的代码,往往具有更高的稳定性。防御编程
    发表于 12-15 07:20

    ARM嵌入编程之STM32的命名方法

    ARM嵌入编程之STM32的命名方法:ARM嵌入编程之STM32的命名方法
    发表于 12-22 06:29

    何为断言断言该怎么使用呢

    存在错误。因此,断言是提高程序可靠性的有效手段。也是开发阶段快速定位问题的一种很好防御编程方法。在C语言中,断言是一些条件判断的宏。比如C
    发表于 09-21 14:59

    怎么理解Assert中的断言语句?

    为什么项目中的代码需要有Assert断言语句?
    的头像 发表于 03-03 14:12 2741次阅读

    STM32函数库Assert断言机制

    编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任
    发表于 02-08 15:29 2次下载
    STM32函数库<b class='flag-5'>Assert</b><b class='flag-5'>断言</b>机制

    RT-Thread 大会:utest提供的断言方法

    utest提供的断言方法:Utest单元测试框架提供了-系列的assert 宏,用于测试用例判断运行结果。
    的头像 发表于 05-27 16:23 1221次阅读
    RT-Thread 大会:utest提供的<b class='flag-5'>断言</b>方法

    C语言断言函数assert()的应用,清晰明了!

    这样可以快速发现并定位软件问题,同时对系统错误进行自动报警。对于在系统中隐藏很深,用其他手段极难发现的问题也可以通过断言进行定位,从而缩短软件问题定位时间,提高系统的可测性。
    的头像 发表于 04-12 10:02 1103次阅读