zoukankan      html  css  js  c++  java
  • C标准库<assert.h>实现

    本文地址:http://www.cnblogs.com/archimedes/p/c-library-assert.html,转载请注明源地址。

    1.背景知识

    头文件<assert.h>唯一的目的就是提供assert宏定义,可以在程序中关键的地方使用这个宏来进行断言。如果一处断言被证明非真,希望程序在标准错误流输出一条适当的提示信息,并使执行异常终止。

    可以这样写代码:

    #include<assert.h>
    ...
    assert(0 <= i && i < sizeof(a) / sizeof(a[0]));

    当然上面的代码不是实战中的最好的形式,程序异常终止应该改为某种错误的恢复。

    宏NDEBUG

    可以通过在程序的某些地方定义宏NDEBUG来改变assert的展开方式

    如果程序某个包含assert的地方没有定义NDEBUG,该头文件就会将宏assert定义为活动形式,它就可以展开为一个表达式,测试断言并在断言为假的时候输出一条错误信息,然后程序终止。反之,如果定义了NDEBUG,头文件就会把这个宏定义为不执行任何操作的静止形式。

    2.<assert.h>的使用

    从上面的代码中可以看到,可以使用一个简单的谓词来简化assert:

    if(!ok)
        abort();  //在头文件<stdlib.h>中声明

    如果觉得断言没有存在的必要,就在包含头文件之前加上下面的代码:

    #define NDEBUG //取消断言
    #include<assert.h>

    可以在整个源文件中用不同的方式控制断言,当断言在频繁执行的循环内部发生时,性能可能会急剧下降,或在达到提示性的部分之前,一个更早的断言可能会终止程序。要打开断言,可以写:

    #undef NDEBUG
    #include<assert.h>

    要关闭断言,可以写:

    #define NDEBUG
    #include<assert.h>

    注意:即使宏NDEBUG已经被定义了,我们仍然可以安全地定义它,这是一个良性重定义

    3.<assert.h>的实现

    从上面的分析知该头文件的大致框架如下:

    #undef assert  //消除已定义的
    #ifdef NDEBUG
    #define assert(expr)  ((void) 0)  //功能失效
    #else
    #define assert (expr) ...
    #endif

    一个简单的编写宏assert的活动形式的方式如下:

    #define assert(expr) if(!(expr)) 
        fprintf(stderr, "Assertion failed: %s, file %s, line %i
    ", 
            #expr, __FILE__, __LINE__)

    这种方式因为如下几种原因不能接受:

    1、宏不能直接调用库的任何输出函数

    上面的定义中包含fprintf、stderr等在stdio.h中定义的函数或宏,程序可能没有包含这个头文件

    2、宏必须能扩展为一个void类型的表达式

    3、宏应该可以扩展为有效并且紧凑的代码

    这个版本却总是调用了一个传递了5个参数的函数

    修改后的assert宏如下:

    #undef assert
    #ifdef NDEBUG
        #define assert(expr)  ((void) 0)
    #else
        void __bad_assertion (const char *_mess);
        #define    __str(x)    # x
        #define    __xstr(x)    __str(x)
        #define    assert(expr)    ((expr)? (void)0 : 
                    __bad_assertion("Assertion "" #expr 
                        "" failed, file " __xstr(__FILE__) 
                        ", line " __xstr(__LINE__) "
    "))
    #endif

    其中__LINE__ 是内置宏,代表该行代码的所在行号,由于__LINE__没有扩展成字符串字面量,它变成了一个十进制常量,把它转换成适当的形式需要一个额外的处理层。向头文件中添加两个隐藏的宏__str和__xstr来实现,其中一个宏用它的十进制常量扩展来取代__LINE__,另一个是把十进制常量转换成一个字符串字面量

    宏调用的隐藏库函数__bad_assertion的实现:

    #include<assert.h>
    #include<stdio.h>
    #include<stdlib.h>
    void __bad_assertion(const char *mess) {
            fputs(mess, stderr);
            abort();
     }

    函数__bad_assertion使用了两个其他的库函数,通过调用<stdio.h>中声明的函数fputs把字符串写到标准错误流,并使用abort异常终止程序的执行,有关这些相关头文件以后会详细剖析。

    4.<assert.h>的测试

    #include<assert.h>
    #include<stdio.h>
    #include<stdlib.h>
    int main( void )
    {
           FILE *fp;
           fp = fopen( "test.txt", "w" );//以可写的方式打开一个文件,如果不存在就创建一个同名文件
           assert( fp );                           //所以这里不会出错
           fclose( fp );
        
           fp = fopen( "noexitfile.txt", "r" );//以只读的方式打开一个文件,如果不存在就打开文件失败
           assert( fp );                           //所以这里出错
           fclose( fp );                           //程序永远都执行不到这里来
           return 0;
    }

    注意:

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

    int resetBufferSize(int nNewSize)
    {
      //功能:改变缓冲区大小,
      //参数:nNewSize 缓冲区新长度
      //返回值:缓冲区当前长度 
      //说明:保持原信息内容不变     nNewSize<=0表示清除缓冲区
      assert(nNewSize >= 0);
      assert(nNewSize <= MAX_BUFFER_SIZE);
      ...
    }

    2.每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败,如:

    assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);//不好
    
    //
    assert(nOffset >= 0);
    assert(nOffset+nSize <= m_nInfomationSize);

    3.不能使用改变环境的语句,因为assert只在DEBUG个生效,如果这么做,会使用程序在真正运行时遇到问题,如:

    错误:

    assert(i++ < 100);

    这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。

    正确:

    assert(i < 100);
    i++;

    4.assert和后面的语句应空一行,以形成逻辑和视觉上的一致感。

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

    参考资料

    《C标准库》

  • 相关阅读:
    Activiti 整合的小插曲
    IDEA 提示找不到 javax 等 tomcat 的相关包
    一些好用的 Oracle 批处理和语句
    Oracle 日志报错导致的 “没有登录” 问题
    WebPack 从安装到闲置
    CHENEY-YANG'S BLOG(cheney-yang)
    Java基础知识常识总结
    激活Navicat
    IDEA文件头版权模板
    关于Spring框架中StringUtils常用/易误用方法解析
  • 原文地址:https://www.cnblogs.com/wuyudong/p/c-library-assert.html
Copyright © 2011-2022 走看看