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

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

3天内不再提示

【C语言进阶】利用assert高效排查你的C程序

嵌入式物联网开发 来源:嵌入式物联网开发 作者:嵌入式物联网开发 2022-08-31 13:27 次阅读
众所周知,我们在实际开发C程序的时候,往往是编码容易——调试困难,修改容易——排查困难。我们在开发过程中,debug占据了我们很大一部分的时间,而正确地使用各种编码手段,可以有效地提升排查问题代码的效率。笔者从自己的实践经验出发,给大家分享一个用于编码/调试阶段高效发现问题代码的利器,这就是大名鼎鼎的**assert**。通过阅读本文,你将了解到以下内容:
  • 什么是assert?
  • assert有什么用?
  • assert怎么使用?
  • assert的常规操作有哪些?

什么是assert?


​ assert它的中文含义是“断言”,它被包含在中,往往给使用者呈现的形式为: assert() 。因此,很多开发者认为它就是一个函数,可能它的原型就是void assert(int expression); 但研究过assert.h的,一定会发现,其实并不是。

​ assert的真身,其实是一个宏定义,只不过是一个带参数输入的宏定义,与我们之前一篇八卦Linux内核设计的max宏定义 (【Linux内核】从小小的宏定义窥探Linux内核的精妙设计)类似的。庐山真面目如下所示:

#define assert(e) ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zc6EAp8U-1661923571346)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

​ 从它的定义,我们可以很清晰的知道,真正起到打印作用的是_assert,而它才是真正的一个函数。原型为:

void _assert(const char *e, const char *file, int line);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dax1ldZf-1661923571352)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]


assert有什么用?


​ 本文的主题是利用assert高效排查问题代码,自然assert的用途就是排查代码;但是,具体它的功能是怎么体现呢?假设有如下代码,一个测试函数的实现片段:

int test_function(int a, int *b)
{
    assert(a > 1);  /* 断言:入参a的值一定大于1 */
    assert(b);      /* 断言: 入参b指针一定不是NULL */

    /* Do other things here ... */
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ejym7Jij-1661923571353)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

​ 如代码所示,有一个测试函数test_function,接收2个入参,一个是int型的a变量,一个int *类型的b指针;在函数的开始,我们就用assert分别对a和b做了断言,确保它们有正确的输入。假设我们有如下的函数调用的测试代码:

{
    int a = 7;
    int *b = &a;
    
    test_function(a, b);

    /* Do other things here ... */
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E4QQ143R-1661923571355)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

​ 很明显,当如上代码调用test_fucntion时,内部的两个assert判断均为【真】,那么什么事情也不会发生,assert就像一个优雅的淑女,静静地站在那里看着你。

​ 当我们的测试代码做如下调整:

{
    int a = 0;
    int *b = &a;
    
    test_function(a, b);

    /* Do other things here ... */
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPbDKDBb-1661923571357)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

​ 很明显,test_function的第一个assert语句不为【真】,那么它就像山洪一样要爆发了,终止程序运行的同时,会输出类似的错误提示: Assertion failed: a > 1,file xxx.c, line 128,这段错误提示,不仅告诉了我们哪个条件判断出错了,并且还告诉了我们出错的位置在哪个文件的哪一行,这是多么智能啊!由此可见,它真正的威力在于【代码出错】时,即当代码没有按照我们的断言进行时,我们就应该停下来,排查下为何会有错误的参数输入,这样我们就可以将bug在出现苗头的时候就把它消灭掉。


assert怎么使用?


​ 其实,上面的示例代码已经展示了如何使用assert,但是我们需要补充的是,一般在使用assert断言语句的时候,需要在对应的.c文件加上对assert.h的引用,否则编译会报错误。

​ assert这么智能的利器是非常有利于我们写出高质量不易出错的代码的,通常富有经验的程序员都会很擅长使用assert语句,把assert打在恰当的语句中,可以最大限度地提升我们的代码质量。但是,很多开发者开始有疑惑了,要是每条语句,每个判断都加上assert,那么就算全部assert的情况都是【真】,也够CPU忙一会了,这样似乎有些浪费CPU的计算能力,以追求高效的C语言编程,可容不下这样的事情发生。那,这可怎么办呢?

​ 为避免以上情况的发生,我们作为assert的使用者,一般只需要在开发调试阶段才使用assert,而在正式发布的版本是需要去掉assert的。这样疑惑就更大了,发布版本一条条删掉assert调用,万一删错了代码呢?设计者总是聪明的,他们也早就想到了这一点,这不他们也提供了解决方案。开头的时候,我们介绍了assert是一个宏,但并没有完全展示它的全貌。现在开始展示它的真容:

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69cPczpO-1661923571359)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

​ 聪明的你,一定也发现了,我们只需要在.c文件#include 之前,加上一句#define NDEBUG 1就可以把相应.c中的assert(e)全部变成((void)0);而((void)0)本身是个无效调用代码,在实际的编译过程中是会被优化掉的,这样我们仅增加对NDEBUG(NO DEBUG的意思)的宏定义,就可以把全部的assert给摒弃了,是不是很智能呢?


assert的常规操作有哪些?


​ 正如上面所述,assert这么智能,但是我们也不能滥用,只需要在恰当的位置作为特定的判断;通常来说,我们有以下一些情景可以考虑使用assert语句:

  • 函数的入参判断,对错误的入参及时处理
  • 对重点调用的系统函数的返回结果做判断,使用assert保证系统调用的结果是正确的,避免外部使用不正确的系统调用而出现错上加错的情况;
  • switch语句中,如果不允许出现default的情况,可以考虑在default分支中加入assert(0);
  • 执行计算时,做计算的输入或计算结果的输出等做下判断,比如除数不能为0,比如一个百分比值不能超过100%等等。

​ 综述,assert是把双刃剑,出错时它能很优秀地暴露问题代码,非常有利于我们排查代码,从而以最快的速度找到问题并解决问题;同时,它的频繁调用,一定程度上加上了CPU的处理,做一些无畏的判断,“简直就是在浪费生命”。所以,在实际开发过程中,我们务必要严谨细致地使用assert,让它更好地为我们服务。

​ 只有不自负且思维严谨的人才能使用好assert,我们只有做到了不自负,不对自己的代码打100%的包票,相信是代码总会有出错的时候,才会逐步养成思维严谨的习惯,反而对自己的代码质量有更大的提升。

​ 本文对assert的介绍和使用做了一番总结,文中难免有纰漏之处,还望读者诚心指正,感谢。

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

    关注

    180

    文章

    7604

    浏览量

    136686
  • C程序
    +关注

    关注

    4

    文章

    254

    浏览量

    36027
  • ASSERT
    +关注

    关注

    0

    文章

    17

    浏览量

    7239
收藏 人收藏

    评论

    相关推荐

    C语言assert的使用

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

    C语言assert(断言)简介

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

    C语言进阶

    C语言进阶见附件
    发表于 08-13 15:51

    C语言进阶书分享!

    挺好的。c语言进阶.pdf (1.78 MB )
    发表于 10-16 02:44

    100个经典C语言程序

    c语言编写,c语言的100个经典程序,单片机的应用,开发利用
    发表于 12-17 11:46 11次下载

    时钟设计程序C语言

    时钟设计程序。时钟设计程序。时钟设计程序C语言】时钟设计程序
    发表于 12-28 12:02 0次下载

    单片机IO扩展(进阶)程序集合【C语言+汇编】

    单片机IO扩展(进阶)程序集合【C语言+汇编】
    发表于 01-06 11:03 8次下载

    单片机IO扩展(进阶)程序集合【C语言

    单片机IO扩展(进阶)程序集合【C语言】。
    发表于 01-06 11:04 23次下载

    DSP C2000程序员高手进阶

    DSP C2000程序员高手进阶 PDF 版
    发表于 05-06 15:13 33次下载

    DSP C2000程序员的高手进阶

    DSP C2000程序员的高手进阶
    发表于 10-16 13:16 20次下载
    DSP <b class='flag-5'>C</b>2000<b class='flag-5'>程序</b>员的高手<b class='flag-5'>进阶</b>

    C语言进阶学习课件资料合集

    本文档的主要内容详细介绍的是C语言进阶学习课件资料合集包括了:第1节-数据的存储,第2节-指针的进阶,第3节-字符串+内存函数的介绍,第4节-自定义类型详解(结构体+枚举+联合),第
    发表于 07-14 08:00 11次下载
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>的<b class='flag-5'>进阶</b>学习课件资料合集

    C语言进阶】sprintf和snprintf的区别

    C语言进阶】sprintf 和 snprintf 真的没有区别吗?
    的头像 发表于 08-31 13:18 1.2w次阅读

    C语言进阶C语言指针的高阶用法

    C语言进阶C语言指针的高阶用法
    的头像 发表于 08-31 13:24 2317次阅读

    C语言进阶之嵌入式系统高级C语言编程

    电子发烧友网站提供《C语言进阶之嵌入式系统高级C语言编程.rar》资料免费下载
    发表于 11-18 10:32 1次下载
    <b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>进阶</b>之嵌入式系统高级<b class='flag-5'>C</b><b class='flag-5'>语言</b>编程

    C语言构建高效的嵌入式程序

    嵌入式工程师在编写C语言程序时,需要注重效率和清晰的思路。本文将通过解析经典问题“猴子选大王”来展示如何用C语言思维方式构建
    的头像 发表于 12-21 09:27 627次阅读