assert宏的实现

在看《编写高质量的C语言代码》中第二章提到,使用断言,于是翻出以前看过的《C标准库》查看assert宏的实现。

这篇文章主要内容来自 《C标准库》

1.<asssert.h>

头文件<assert.h>中除了定义宏assert以外还引用了另外一个宏 NDEBUG,后者不是定义在<assert.h>中的。如果NDEBUG出现在任何包含<assert.h>的文件中,并被定义为宏名,那么宏assert就直接被定义为:

#define assert(_exp) ( (void)0 )

宏assert应该作为一个宏,而不是一个实际的函数来实现。

2.assert 的使用

一个断言可以简写为:

if(!okay){
	abort();
}

应该记住assert宏仅仅是帮助记住源代码中作的假设,一个产品程序不能这样突然间终止,无论断言在调试的时候是多么的方便,最终他们都是多余的。

怎样控制宏的展开方式不过是一个风格问题,但是必须通过某种方式来控制DDEBUG是否定义。其中一种方式是修改源代码,如果你认为没有存在的必要了,则只需要在包含<assert.h>的源文件前面加上下面代码就可以了

#define NDEBUG  /* disable assertions */
#include <assert.h>
这段代码清楚的说明了断言从此不再起作用,而当退回来重新进行到调试时,这种风格的缺陷也就爆出来了:此时必须重新编辑源代码。
普遍的编译器支持一种更为灵活的方法,它们允许在所有源文件之外定义宏,可以在make文件中定义/不定义来解决。

我们可以在源文件不同地方用不同的方式来控制断言,所以需要我们在一个源文件中各个不同地方打开或者关闭断言。

打开/关闭 断言可以写成:

/*able*/
#undef NDEBUG
#include <assert.h>

/*disable*/
#define NDEBUG
#include <assert.h>
这段代码中,可能会造成:对NDEBUG进行重定义或取消没有定义的NDEBUG宏,但他们是良性重定义和良性取消定义。

3.assert设计的注意事项

为了对NDEBUG作出响应,所以该文件有一个总体结构

#undef assert

#ifdef NDEBUG
#define assert(_exp) ((void)0)
#else
#define assert(_exp) ...
#endif
这又是一个良性取向定义,因为:总是可以#undef 一个名字,无论它是不是被定义为一个宏。

一个简单的编写宏的活动形式的方式是:

#define assert(_exp) if((!_exp)) \
	fprintf(stderr,"Assertion failed:%s,file %s,line %i\n",#test,\
	__FILE__,__LINE__) /*unacceptable*/
但这段代码是不可接受的,因为:

(1)宏不能直接调用库的任何函数,例如fprintf,也不能引用宏stderr。因为这些名字都是在<stdio.h>中或其他文件中正确声明或者定义的,程序可能没有包含这些文件,但<assert.h>中一定不能包含这些头文件。一个程序如果不包含任何头文件,就可以定义宏来对该头文件中任意名字重命名。这就要求宏必须调用一个具有隐藏名字的函数来进行实际输出。

(2)宏必须能扩展为一个 void 类型的表达式。例如程序中,包含形如 (assert(0<x),x<y)的表达式,这就不能使用if语句了,任何条件测试都要在一个表达式内部使用某个条件操作符。

(3)宏应该可以扩展为有效并紧凑的代码,否则程序员会尽量避免使用这个宏。而这个版本却使用了传5个参数的函数。

4.assert的实现----《C标准库》

宏的定义:

/*assert.h*/
/*<<C标准库>>*/

#undef assert

#ifdef NDEBUG
	#define assert(test) ((void)0)
#else
	void _Assert(char *);
	#define _STR(X) _VAL(X)
	#define _VAL(X) #X
	#define assert(test) ( (test)? (void)0\
	:_Assert(__FILE__":"_STR(__LINE__)" " #test) )
#endif
注意在这,__LINE__是一个整数,在_STR(__LINE__)中用实际的数字来替代参数并调用_VAL(X) #X转换为字符串,因为嵌套宏展开顺序是:替换外层宏,传入实际参数替代形参,替换内层宏


_Assert(char *)这个隐藏的函数的实现:


/*_Assert funcion*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

void _Assert(char *mesg){
		fputs(mesg,stderr);
		fputs("----------assertion failed \n",stderr);
		abort();
}

5.VS2008的实现

查看了Microsoft Visual Studio 9.0\VC\crt\src中assert.h中的实现

#define assert(_Expression) (void)( (!!(_Expression)) \
|| (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
使用:||运算的截断 和  !!(_Expression)两次取否,这两点用得太精辟了。

很明显,也使用了隐藏函数_wassert()

你可能感兴趣的:(File,Microsoft,活动,扩展,语言,编译器)