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

众所周知,我们在实际开发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的介绍和使用做了一番总结,文中难免有纰漏之处,还望读者诚心指正,感谢。


华为放弃代工谷歌Pixel原因?证实因Logo禁令
中兴通讯首席运营官谢峻石:坚持“创新与协同” 携手推动高质量发展
高频DC-DC转换器中的电感器性能
vivox9总想搞点事!vivox9磨砂黑限量似乎有那么一点惊艳
国家加快推进新基建建设的目的是什么
【C语言进阶】利用assert高效排查你的C程序
峰值电流控制型芯片UC3842的内部工作原理
MPU6050简介及rt-thread软件包使用
土豪看过来,iPhone7 Plus对阵Galaxy S8+,到底谁更强?IOS当真已经被安卓超过了吗?
南京审计大学携手诚迈科技探索审计大模型,联合打造审计智人
普惠与南航签署了一份为期12年的综合服务协议
东芝推出栅驱动器开关IPD,待机状态下的耗电量仅为3μA
码垛机器人已经成为了产业升级的重要推力
降压开关控制器70V转5V 1A电源芯片
从2D到3D,沉浸式的实时视频通信是如何实现的?
关于人员定位追踪系统的详细说明
细数毫米波雷达在中国的这几年
新触控技术将使新款iPhone、iPad更有趣
P89C51RD2隐藏分区的读取方法
ldo内部结构和工作原理