C语言解读assert函数

  • 在 C 语言中,断言被定义为宏的形式(assert(expression)),而不是函数,其原型定义在文件中。其中,assert 将通过检查表达式 expression 的值来决定是否需要终止执行程序。也就是说,如果表达式 expression 的值为假(即为 0),那么它将首先向标准错误流 stderr 打印一条出错信息,然后再通过调用 abort 函数终止程序运行;否则,assert 无任何作用。
#include 
void assert( int expression );
  • 默认情况下,assert 宏只有在 Debug 版本(内部调试版本)中才能够起作用,而在 Release 版本(发行版本)中将被忽略。当然,也可以通过定义宏或设置编译器参数等形式来在任何时候启用或者禁用断言检查(不建议这么做)。同样,在程序投入运行后,最终用户在遇到问题时也可以重新起用断言。这样可以快速发现并定位软件问题,同时对系统错误进行自动报警。对于在系统中隐藏很深,用其他手段极难发现的问题也可以通过断言进行定位,从而缩短软件问题定位时间,提高系统的可测性。
  • 已放弃使用assert()的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。在调试结束后,可以通过在包含#include 的语句之前插入 #define NDEBUG 来禁用assert调用,示例代码如下:
#include 
#define NDEBUG
#include 
  • 尽量在函数中使用断言来检查参数的合法性
    在函数中使用断言来检查参数的合法性是断言最主要的应用场景之一,它主要体现在如下 3 个方面:

    1. 在代码执行之前或者在函数的入口处,使用断言来检查参数的合法性,这称为前置条件断言。
    2. 在代码执行之后或者在函数的出口处,使用断言来检查参数是否被正确地执行,这称为后置条件断言。
    3. 在代码执行前后或者在函数的入出口处,使用断言来检查参数是否发生了变化,这称为前后不变断言。
  • Memcpy 函数中,除了可以通过“assert(dest !=NULL&&src!=NULL);”语句在函数的入口处检查 dest 与 src 参数是否传入 NULL 指针之外,还可以通过“assert(tmp_dest>=tmp_src+len||tmp_src>=tmp_dest+len);”语句检查两个内存块是否发生重叠。如下面的示例代码所示:

void *Memcpy(void *dest, const void *src, size_t len)
{
        assert(dest!=NULL && src!=NULL);
        char *tmp_dest = (char *)dest;
        char *tmp_src = (char *)src;
        /*检查内存块是否重叠*/
        assert(tmp_dest>=tmp_src+len||tmp_src>=tmp_dest+len);
        while(len --)
                *tmp_dest ++ = *tmp_src ++;
        return dest;
}
  • 除此之外,建议每一个 assert 宏只检验一个条件,这样做的好处就是当断言失败时,便于程序排错。试想一下,如果在一个断言中同时检验多个条件,当断言失败时,我们将很难直观地判断哪个条件失败。因此,下面的断言代码应该更好一些,尽管这样显得有些多此一举:
assert(dest!=NULL);
assert(src!=NULL);
  • 最后,建议 assert 宏后面的语句应该空一行,以形成逻辑和视觉上的一致感,让代码有一种视觉上的美感。同时为复杂的断言添加必要的注释,可澄清断言含义并减少不必要的误用。

你可能感兴趣的:(C语言进阶,c语言)