1.前言2.assert断言3.ifVSassert4.总结1.前言我们在写代码的时候,经常需要代码安全例如:1.指针是否为空?2.分红是0吗?3、函数调用的返回结果是否有效?4、打开文件是否成功?这种边界条件检查的手段,一般使用if或者assert,无论使用哪一种都可以达到检查的目的。那么是不是说:这两个可以随便用,想到哪个就用哪个?这篇小文来分解一下:在不同的场景下,我们应该使用if还是assert?写这篇文章的时候,我想到了孔乙己先生的问题:茴香豆的“分”字有几种写法?看来我们不用纠结如何选择了,因为都可以达到想要的功能。我以前也这么认为,但现在我不这么认为了。成为技术大师并获得更好的报价可能是这些微妙的结果。2、Assert断言刚才问了旁边一位工作5年多的嵌入式开发人员:if和assert怎么选择?他说:assert是做什么的?!看来有必要先简单说一下assert断言。.assert()的原型是:voidassert(intexpression);1.如果宏参数的计算结果为非零值,则什么都不做(无动作);2.如果宏参数为零值,则打印诊断信息,然后调用abort()。例如下面的代码:#includeintmy_div(inta,intb){assert(0!=b);returna/b;}1.当b不为0时,assert什么都不做,程序继续to2.当b为0时,assert断言打印错误信息,然后终止程序;从功能上讲,assert(0!=b);相当于下面的代码:if(0==b){fprintf(stderr,"biszero...");abort();}assert是宏,不是函数在assert.h头文件中,是定义如下:#ifdefNDEBUG#defineassert(condition)((void)0)#else#defineassert(condition)/*implementationdefined*/#endif既然是宏定义,说明在预处理的时候宏被替换掉了。从上面的定义可以看出:如果定义了NDEBUG这个宏,那么assert()这个宏就不会做任何事情,相当于一个空语句:(void)0;,在release阶段编译代码的时候,它将在编译选项(Makefile)中定义这个宏。如果宏NDEBUG没有定义,那么assert()宏将替换一些检查代码。我们在开发阶段用debug模式编译的时候,一般会屏蔽NDEBUG宏。3.如果VSassert还是用代码片段来描述问题,在场景化的讨论中更容易理解。//brief:将两个短字符串连接成一个字符串char*my_concat(char*str1,char*str2){intlen1=strlen(str1);intlen2=strlen(str2);intlen3=len1+len2;char*new_str=(char*)malloc(len3+1);memset(new_str,0len3+1);sprintf(new_str,"%s%s",str1,str2);returnnew_str;}如果开发者写了上面的代码,肯定会被面试领导人!它存在以下问题:没有对输入参数进行合法性检查;没有检查malloc的结果;sprintf的效率很低;...1。使用if语句检查char*my_concat(char*str1,char*str2){if(!str1||!str2)//参数错误returnNULL;intlen1=strlen(str1);intlen2=strlen(str2);intlen3=len1+len2;char*new_str=(char*)malloc(len3+1);if(!new_str)//申请堆空间失败returnNULL;memset(new_str,0len3+1);sprintf(new_str,"%s%"s",str1,str2);returnnew_str;}2.使用assert断言检查char*my_concat(char*str1,char*str2){//确保参数正确assert(NULL!=str1);assert(NULL!=str2);intlen1=strlen(str1);intlen2=strlen(str2);intlen3=len1+len2;char*new_str=(char*)malloc(len3+1);//确保堆空间申请成功assert(NULL!=new_str);memset(new_str,0len3+1);sprintf(new_str,"%s%s",str1,str2);returnnew_str;}3.你喜欢哪一个?先声明:以上两种检查方式在实际代码中很常见,貌似对功能没有影响。所以,并没有严格的是非之分,很多还是要看每个人的喜好。(1)Assertsupporter作为my_concat()函数的实现者,目的是拼接字符串,所以传入的参数必须合法有效,调用者需要对此负责。如果传入的参数无效,我会很惊讶!怎么做:我会告诉你崩溃!(2)如果我写的my_concat()函数非常健壮,我估计调用者会胡乱乱来,故意传入一些无效的参数来测试我的编码水平。没关系,加油,我能应付任何情况!这两派的理由,似乎已经足够了!那么如何选择呢?你真的跟随感觉吗?假设我们严格按照常规流程开发一个项目:1.在开发阶段,如果编译选项中没有定义宏NDEBUG,那么assert就会起作用;2.项目发布时,如果在编译选项中定义了NDEBUG。在开发阶段,只有使用assert断言才能正确检查无效参数。在发布阶段,assert不起作用。如果调用者传递无效参数,程序只会崩溃。这是什么意思?代码中有错误吗?还是代码不够健壮?个人理解,这根本上是单元测试没写好,不存在参数无效的情况!4、assert的本质assert就是验证有效性,它最大的作用是:在开发阶段,尽可能的让我们的程序崩溃。每次崩溃都意味着代码中存在错误,我们需要修复它。当我们写一个assert断言的时候,就是说断言失败是不允许的,是不允许的。必须保证断言成功,程序才能继续执行。5、if-else的本质if-else语句用于逻辑处理,是处理各种可能的情况。也就是说:每一个分支都是合理的,允许出现的,我们要处理这些分支。6.我喜欢的版本char*my_concat(char*str1,char*str2){//参数必须有效assert(NULL!=str1);assert(NULL!=str2);intlen1=strlen(str1);intlen2=strlen(str2);intlen3=len1+len2;char*new_str=(char*)malloc(len3+1);//申请堆空间失败是可能的,也是允许的。如果(!new_str)返回NULL;memset(new_str,0len3+1);sprintf(new_str,"%s%s",str1,str2);returnnew_str;}对于参数:我认为传入的参数必须是有效的,如果出现无效参数,说明代码有bug,是不允许的,必须解决。关于资源分配结果(malloc函数):我认为资源分配失败是合理的、可能的、允许的,我也处理过这种情况。当然并不是说要用assert做参数检查,主要是根据不同的场景和语义来判断。例如下面的例子:intg_state;voidget_error_str(boolflag){if(TRUE==flag){g_state=1;assert(1==g_state);//确保赋值成功}else{g_state=0;assert(0==g_state);//确保赋值成功}}flag参数代表不同的分支情况,赋值给g_state后,必须保证赋值结果的正确性,所以使用了assert。4.小结本文分析了C语言中一个比较晦涩模糊的概念。看起来有点虚幻,但确实需要我们停下来仔细想想。如果有一些场景真的很难处理,我会问自己一个问题:这种情况允许吗?不允许:只使用assert断言,在开发阶段尽量找出所有的错误情况;allow:只用if-else,说明这是合理的逻辑,需要下一步处理。本文转载自微信公众号“IOT物联网小镇”,可通过以下二维码关注。转载本文请联系物联小镇公众号。