一. assert的作用

if 的作用基本无异,都用来检测一些边界条件和进行安全性检查,如:

  1. 指针是否为空?
  2. 被除数是否为 0?
  3. 函数调用的返回结果是否有效?
  4. 打开一个文件是否成功?

使用格式:assert(exp);

assert 会计算表达式 expression ,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort终止程序运行

从功能上而言,下面的两种写法等效:

1
2
3
4
5
6
7
assert(0 != b);
//上下两者在功能上等价
if (0 == b)
{
fprintf(stderr, "b is zero...");
abort();
}

同时需要注意,assert 是宏,而非函数:

1
2
3
4
5
#ifdef NDEBUG
#define assert(condition) ((void)0)
#else
#define assert(condition) /*implementation defined*/
#endif

从上面assert的宏可看出:

  1. 如果定义了宏 NDEBUG,那么 assert() 宏将不做什么动作,也就是相当于一条空语句: (void)0;当在 release 阶段编译代码的时候,都会在编译选项中(Makefile)定义这个宏
  2. 如果没有定义宏 NDEBUG,那么 assert() 宏将会把一些检查代码进行替换,我们在开发阶段执行 debug 模式编译时,一般都会屏蔽掉这 NDEBUG 这个宏 。一般来说断言 assert() 是仅在 Debug 版本起作用的宏。在发布版本时,我们不应该再依赖 assert() 宏,因为程序一旦出错,assert() 会抛出一段用户看不懂的提示信息,并毫无预警地终止程序执行,这样会严重影响软件的用户体验,所以在发布模式下应该让assert()失效。另外在程序中频繁的调用 assert() 会影响程序的性能,增加额外的开销。

二. assert与if的区别

在功能上没有明显区别。 二者在工程代码中都很常见,没有对错之分,更多只是编程风格和习惯上的差异。

(1) assert 支持者

我作为 my_concat() 函数的实现者,目的是拼接字符串,那么传入的参数必须是合法有效的,调用者需要负责这件事 。如果传入的参数无效,我会表示十分的惊讶!怎么办:崩溃给你看!

(2)if 支持者

我写的 my_concat() 函数十分的健壮,我就预料到调用者会乱搞,故意的传入一些无效参数,来测试我的编码水平。没事,来吧,我可以处理任何情况!

从上文我们得知,assert 仅在 debug 模式下有效, release 模式下不会作用。而 debug 模式是用来排除 bug 的,即排除 非法情况 ,将 所有可能存在 的非法情况全部排除后,才能发行(release)。而如果试图用 if 来判断非法情况并处理错误(以此提高所谓的程序 “健壮性” ),则很可能会隐藏本来就需要排查的非法情况。而这样的程序发行之后,随时会因为某些极端情况而崩溃。

非法情况: 即逻辑层面的 bug 。非法情况是完全不应该出现的情况,逻辑上不允许它的存在。如传入的参数为空指针(在不同的场景下有不同的要求)。

错误情况: 是可以存在,且无法完全避免的情况,在逻辑允许之内。如 malloc() 未申请到内存,返回 NULL

assert 便是用来处理非法情况(验证有效性)它最大作用就是:在开发阶段,让我们的程序尽可能地 crash。每一次的 crash,都意味着代码中存在着 bug,需要去修正。当我们写下一个 assert 断言的时候,就说明:断言失败的这种情况是是不被允许存在的。必须保证断言成功,程序才能继续往下执行。

if 则是用来处理逻辑上各种可能出现的情况,包括错误情况。每一个分支都是合理的,是允许出现的,我们都要对这些分支进行处理。

三. 使用注意事项

  1. 在函数开始处检验传入参数的合法性

    1
    2
    3
    4
    5
    6
    7
    int resetBufferSize(int nNewSize) 
    {
    assert(nNewSize >= 0); //nNewSize<0是非法情况
    assert(nNewSize <= MAX_BUFFER_SIZE);
    if(nNewSize == 0){...} //nNewSize=0是可能的情况
    else{...}
    }
  2. 每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败

    1
    2
    3
    4
    assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);  //不好
    //========================================================
    assert(nOffset >= 0);
    assert(nOffset+nSize <= m_nInfomationSize); //好
  3. 不能使用改变环境的语句,因为assert只在DEBUG个生效,如果这么做,会使用程序在发行后遇到问题

    1
    2
    3
    4
    assert(i++ < 100); //错误
    //========================
    assert(i < 100)
    i++; //正确
  4. assert和后面的语句应空一行,以形成逻辑和视觉上的一致感

  5. 有的地方,assert不能代替条件过滤

    这需要分清 非法情况错误情况


如有错误,烦请读者指出。