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\n", \#expr, __FILE__, __LINE__)

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

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

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

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

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

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

修改后的assert宏如下:

#undef assert
#ifdef NDEBUG#define assert(expr)  ((void) 0)
#elsevoid __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异常终止程序的执行,有关这些相关头文件以后会详细剖析。

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标准库》

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

C标准库assert.h实现相关推荐

  1. C 标准库 - assert.h

    C 标准库 - <assert.h> 简介 C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息. 已定义的宏 a ...

  2. C 标准库—— assert.h

    C 标准库-- string.h C 标准库 -- time.h C 标准库 -- limits.h C 标准库 -- stdio.h C 标准库-- stdlib.h(包括 rand srand 的 ...

  3. c语言标准库assert,C 标准库 - assert.h

    assert.h是c标准库的一个头文件,该头文件的主要目的就是提供一个assert的宏定义. assert只是对所给的表达式求值,就像if判断语句中一样,然后如果该值为真则正常运行,否则报错,并调用a ...

  4. C 标准库 —— limits.h

    C 标准库-- string.h C 标准库 -- time.h C 标准库 -- limits.h C 标准库 -- stdio.h C 标准库-- stdlib.h(包括 rand srand 的 ...

  5. C 标准库 —— stdio.h

    C 标准库-- string.h C 标准库 -- time.h C 标准库 -- limits.h C 标准库 -- stdio.h C 标准库-- stdlib.h(包括 rand srand 的 ...

  6. C 标准库 —— time.h

    C 标准库-- string.h C 标准库 -- time.h C 标准库 -- limits.h C 标准库 -- stdio.h C 标准库-- stdlib.h(包括 rand srand 的 ...

  7. C 标准库—— string.h

    C 标准库 -- string.h C 标准库 -- time.h C 标准库 -- limits.h C 标准库 -- stdio.h C 标准库-- stdlib.h(包括 rand srand ...

  8. C 标准库—— stdlib.h(包括 rand srand 的实现)

    C 标准库-- string.h C 标准库 -- time.h C 标准库 -- limits.h C 标准库 -- stdio.h C 标准库-- stdlib.h(包括 rand srand 的 ...

  9. C 标准库 - ctype.h

    C 标准库 - <ctype.h> 简介 C 标准库的 ctype.h 头文件提供了一些函数,可用于测试和映射字符. 这些函数接受 int 作为参数,它的值必须是 EOF 或表示为一个无符 ...

最新文章

  1. Git学习笔记一--创建版本库、添加文件、提交文件等
  2. CF572_Div2_D2
  3. ajax发送请求-同步和异步
  4. ArcGIS Flex API 中的 Flex 技术(一)--事件
  5. $ is not defined 如何解决
  6. 【转】DLL中类的显式链接
  7. 科大讯飞发布第三季度业绩报告:扣非净利润同比减少近9成
  8. mysql表索引类型修改_MySQL常用的建表、添加字段、修改字段、添加索引SQL语句写法总结...
  9. mro python_一窥Python中MRO排序原理
  10. Java 每半年就会更新一次新特性,再不掌握就要落伍了:Java14 的新特性
  11. oracle静态注册6,oracle数据库静态注册
  12. 阿里云域名配置以及https证书(ssl证书)配置
  13. mhw跳过结尾_怪物猎人世界怎么跳过剧情
  14. The nested type test2 cannot hide an enclosing type
  15. 1253 Dungeon Master
  16. 【哈夫曼树】牛客 哈夫曼树
  17. 边缘计算设备,手机循环经济的新出路
  18. FreeBSD安装各种字体
  19. 录音混音编辑软件-MAGIX Samplitude Pro X6 Suite 17 0.1.21177 x64 WIN
  20. Python实现基于机器学习的手写数字识别系统

热门文章

  1. 编辑docker容器中的文件
  2. 如何利用大数据做金融风控? 原创 2016年11月24日 17:42:03 标签: 大数据 / 金融 / 风控 1594 导语:如何通过海量数据与欺诈风险进行博弈? 随着金融科技、科技金融等概念的
  3. 微软沈向洋:计算机视觉未来在语义层 “两大一精”是关键
  4. Java-Java5.0注解解读
  5. 数据结构-单循环链表(C语言代码)
  6. hamming weight_popcount或者hamming weight(二进制1的个数问题)
  7. Typora最好用的Markdown编辑器
  8. 更新至Android Studio4.1后发现as打不开的解决方案
  9. renderthread是什么_Android 旗舰机标配的高帧屏(120Hz),对各位 App 开发者有什么影响?...
  10. 跨境电商自建站后台系统原型rp_没学历做跨境电商好做吗?虾皮shopee开店没有流水怎么办...