关于c:防御式编程之断言assert的使用

  进攻式编程的重点就是须要进攻一些程序未曾意料的谬误,这是一种进步软件品质的辅助性办法,断言assert就用于进攻式编程,编写代码时,咱们总是会做出一些假如,断言就是用于在代码中捕获这些假如。应用断言是为了验证预期的后果——当程序执行到断言的地位时,对应的断言应该为真;若断言不为真时,程序会终止执行,并给出错误信息。能够在任何时候启用和禁用断言验证,因而能够在程序调试时启用断言而在程序公布时禁用断言。同样,程序投入运行后,最终用户在遇到问题时能够从新启用断言。

1、原型函数

  在大部分编译器下,assert() 是一个宏;在多数的编译器下,assert() 就是一个函数。咱们不须要关怀这些差别,能够只把 assert()当作函数应用即可。即:

void assert(int expression);

  在程序运行时它会计算括号内的表达式,如果 expression为非0阐明其值为真,assert()不执行任何动作,程序继续执行前面的语句;如果 expression为0阐明其值为假,assert()将会报告谬误,并终止程序的执行,值得理解的是,程序终止是调用abort()函数,这个函数性能就是终止程序执行,间接从调用的中央跳出,abort()函数也是规范库函数,在<stdlib.h>中定义。因而assert()用来判断程序中是否呈现了显著非法的逻辑,如果呈现了就终止程序免得导致严重后果,同时也便于查找谬误。

2、具体释义

  assert() 在c规范库中的<assert.h>中被定义。上面就看下在assert.h中的定义:

#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()会影响程序的性能,减少额定的开销。因而能够在<assert.h>中定义NDEBUG宏,将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 <stdio.h>
#include <assert.h>
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 <stdio.h>
#include <assert.h>
void fun(int a, int b)
{
    assert(b != 0);
    int i = a / b;
}

  if是一个关键字,个别用于依据条件来判断逻辑的正确性,即是否依据条件对应执行,Debug和Release版本中都能够应用,例如上面用if的时候,就容许这些判断条件是失常产生的,是正当的,须要依据产生的条件执行对应的逻辑,程序能够往下执行。

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

  因而在应用前,能够先判断下,如果逻辑不容许产生,那么就应用assert在Debug阶段将问题解决掉;如果逻辑容许的,那么就应用if,当然也能够用if判断后进行条件的return操作,来杜绝不容许逻辑,实质是避免谬误的逻辑影响后续程序的执行。例如上述的用来判断除0操作的例子也能够用if:

#include <stdio.h>
#include <assert.h>
void fun(int a, int b)
{
    if(0 == b)
        return;
    int i = a / b;
}

用于判断函数的入参

  个别assert能够用于判断函数入参的合法性,比方入参值是否合乎,指针是否为空:

#include <stdio.h>
#include <assert.h>
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 <stdio.h>
#include <assert.h>
void main()
{
    int i = 0;
    while(i <= 110)
    {
        assert(i++ < 100);
        printf("i = %d\n",i);
    }
}

不要用多个判断条件语句

  个别一个assert只用一个判断语句来实现,如果在一个assert中应用多条判断语句,当谬误产生时,会不晓得是哪个条件语句呈现谬误,谬误体现的就不直观。

#include <stdio.h>
#include <assert.h>
void fun1(int a, int b) //谬误应用
{
    assert(a > 0 && b > 5);
    ...
}
void fun2(int a, int b) //正确应用
{
    assert(a > 0);
    assert(b > 5);
    ...
}

更多技术内容和书籍材料获取敬请关注公众号“明解嵌入式”

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据