C标准库<assert.h>的实现详解


本文实例讲解了C标准库<assert.h>的实现过程及相关用法。分享给大家供大家参考。具体分析如下:

一、背景知识

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

可以这样写代码:

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

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

宏NDEBUG

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

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

二、<assert.h>的使用

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

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

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

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

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

#undef NDEBUG
#include<assert.h>

要关闭断言,可以写:

#define NDEBUG
#include<assert.h>

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

三、<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\n", \
    #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__) "\n"))
#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异常终止程序的执行,有关这些相关头文件以后会详细剖析。

四、<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程序设计的学习有一定的借鉴价值。



相关阅读:
jQuery中ajax - get() 方法实例详解
Win8.1系统在安全模式下杀毒教程
Android 取得状态栏、任务栏高度的小例子
js检测网络是否具体连接功能的代码
用Autoconf检测MySQL软件包的教程
Android获取屏幕或View宽度和高度的方法
php文件服务实现虚拟挂载其他目录示例
C++设计模式之观察者模式
html5配合css3实现带提示文字的输入框(摆脱js)
JavaScript检测字符串中是否含有html标签实现方法
C#的SQL操作类实例
iOS中的NSURLCache数据缓存类用法解析
基于递归实现的php树形菜单代码
利用CSS span实现双语菜单的方法教程
快速导航
PHP MySQL HTML CSS JavaScript MSSQL AJAX .NET JSP Linux Mac ASP 服务器 SQL jQuery C# C++ java Android IOS oracle MongoDB SQLite wamp 交通频道 作文范文 司法局副局长的述职报告 梦已破,我却依旧不愿醒来 酒店公关活动策划书 简单的温暖作文900字 关于工作效能提高执行力的心得体会 正规劳动合同模板 有趣的跳蚤市场作文450字 为人处世十三条铁则 穿梭作文200字 浙江省高考语文试卷特点与答题情况分析 网络祝福短信14 关于认真落实市纪检监察局长在我县调研讲话精神的情况汇报 那些只是树350字 员工出差差旅费管理制度 积雨辋川庄作 / 秋归辋川庄作 失去你的日子里 专业技术职称总结写作格式工作总结 开心闯龙年读后感 林业统计工作总结 书的梦(八) 理解作文650字 将嘴角上扬作文700字 于丹论语人生之道.上. 你可能拥有一段关系,但你未必亲密 小学四年级作文400字:从前有座山(2) 短短几句话,却字字惹人掉泪 默默分手 证券营业服务站主任工作述职报告-述职报告 为你祈祷!我的心陪你一起加油! 滚铁环 美玉的失落 陌上花开,那场说走就走的旅行 我是一名学生作文300字 受人恩惠感谢信 2篇 音乐教学上半年个人工作总结 百度CEO李彦宏的创业传奇故事 市规划局治理商业贿赂专项工作实施方案 我只能在角落里,读你,曾经的温柔。 小学五年级作文450字:我需要一双眼睛 农村的胡同作文300字 ICU岗位护士爱岗敬业演讲稿 初中初一作文650字:漫漫路途弯几何 员工股权激励协议书 80后诗坛领军人物潘建设和他的玄鸟诗社 理事长在全县农村信用社旺季工作会议上的讲话 网络奇缘800字 印象最深刻的一件事400字 第二次恋爱作文500字 生活一壶茶 我的童年离不开书作文450字

Copyright © 2016 phpStudy |