我记得在以前的一篇随笔中,我堆windows下的<assert.h>进行了分析,今天我们来看看gcc中这个文件的定义是怎样的。
【1】assert宏的作用
assert宏实现断言的作用,一般在源文件中引用格式如下:
#include#undef NDEBUGassert(expression)
关于assert宏:
1、当 expression的值为0时进行断言,如果表达式expression的值非零,则不进行断言。
2、assert宏进行断言的时候,在标准错误输出中输出断言发生的源文件名称:__FILE__ 和断言发生时语句所在的行: __LINE__
3、可在程序的调试过程中,利用assert宏进行关键点程序进行测试,以输出一些有用的信息,当不需要调试的时候,可以通过定义NDEBUG宏来取消
宏assert的作用。
【2】assert.h
/* Allow this file to be included multiple times with different settings of NDEBUG. *///assert 为C库提供的一种断言机制//断言用来在标准错误输出流输出信息,并且使程序异常终止/* 断言的机制:*///首先取消 assert 宏的定义,//这样做的目的是为了防止宏重复被定义#undef assert#undef __assert//通过判断是否定义宏 NDEBUG 来判断在源代码中是否需要宏assert/* 如果定义了 NDEBUG 宏,就表示不需要在程序中引用 assert 宏 NDEBUG: do not debug 否则就在程序中,assert 宏将被执行 可以发现assert宏在定义 NDEBUG时,定义很特别,宏参数并没有引用*/#ifdef NDEBUG //定义了NDEBUG宏,assert 宏定义为不做任何提示输出 #define assert(ignore) ((void)0)#else void __eprintf (); /* Defined in gnulib */ #ifdef __STDC__ //定义了__STDC__宏 #define assert(expression) \ ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__))) #define __assert(expression, file, lineno) \ (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ expression, lineno, file), 0) #else /* no __STDC__; i.e. -traditional. */ #define assert(expression) \ ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__))) #define __assert(expression, file, lineno) \ (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ "expression", lineno, file), 0) #endif /* no __STDC__; i.e. -traditional. */#endif
【3】第一段预处理
//首先取消 assert 宏的定义,//这样做的目的是为了防止重复定义宏的影响#undef assert#undef __assert
这样做的目的是为了防止 assert 重定义,引起混乱;这样在引用的位置处,就可以取消前面的定义。
【4】标准assert宏定义结构
通常assert宏定义具有下面的结构
这样结构的目的是为了对用户的 NDEBUG 宏做出正确的响应。
【5】宏代码
//通过判断是否定义宏 NDEBUG 来判断在源代码中是否需要宏assert/* 如果定义了 NDEBUG 宏,就表示不需要在程序中引用 assert 宏 NDEBUG: do not debug 否则就在程序中,assert 宏将被执行 可以发现assert宏在定义 NDEBUG时,定义很特别,宏参数并没有引用*/#ifdef NDEBUG //定义了NDEBUG宏,assert 宏定义为不做任何提示输出 #define assert(ignore) ((void)0)#else void __eprintf (); /* Defined in gnulib */ #ifdef __STDC__ //定义了__STDC__宏 #define assert(expression) \ ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__))) #define __assert(expression, file, lineno) \ (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ expression, lineno, file), 0) #else /* no __STDC__; i.e. -traditional. */ #define assert(expression) \ ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__))) #define __assert(expression, file, lineno) \ (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ "expression", lineno, file), 0) #endif /* no __STDC__; i.e. -traditional. */#endif
这里我们可以看到,这个宏,其实是定义了两个红: assert 和 __assert
1、assert
分两种情况:
定义了 __STDC__ 宏时:
#define assert(expression) \ ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__)))
未定义了 __STDC__ 宏时:
#define assert(expression) \ ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__)))
这两者的区别,仅在于 “#” 连接符的作用,如果不用 #expression 这种形式,则宏参数不能使 带引号的字符串, 而用了
#expression 这种形式,assert宏的实际参数既可以是带引号的字符串。
两个预定义宏:
__FILE__: 返回C成员源文件名
__LINE__ :返回代码行在C文件中的行数
2、__assert
这个宏只有一种形式:
#define __assert(expression, file, lineno) \ (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ "expression", lineno, file), 0)
这个宏,其实是引用了函数 __eprintf() 函数来实现输出,这个函数不是标准的库函数。如果要实现标准assert宏,则不能引用标准库
函数实现,这是因为如果在程序中如果没有包含这些库函数,则会引起异常。
__eprintf() 函数:
#ifdef L_eprintf#include/* This is used by the `assert' macro. */void__eprintf (string, expression, line, filename) char *string; char *expression; int line; char *filename;{ fprintf (stderr, string, expression, line, filename); fflush (stderr); abort ();}#endif
就是说这个头文件还可以这样:
#ifdef NDEBUG //定义了NDEBUG宏,assert 宏定义为不做任何提示输出 #define assert(ignore) ((void)0)#else void __eprintf (); /* Defined in gnulib */ #define __assert(expression, file, lineno) \ (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ expression, lineno, file), 0) #ifdef __STDC__ //定义了__STDC__宏 #define assert(expression) \ ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__))) #else /* no __STDC__; i.e. -traditional. */ #define assert(expression) \ ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__))) #endif /* no __STDC__; i.e. -traditional. */#endif
【6】微软VS 2008 代码比较
/****assert.h - define the assert macro** Copyright (c) Microsoft Corporation. All rights reserved.**Purpose:* Defines the assert(exp) macro.* [ANSI/System V]** [Public]*****/#include#undef assert#ifdef NDEBUG#define assert(_Expression) ((void)0)#else#ifdef __cplusplusextern "C" {#endif_CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line);#ifdef __cplusplus}#endif#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )#endif /* NDEBUG */
不知道,有没有专门介绍gcc编译器源代码的书籍,我知道有一本书gcc internal, 是英语版的,看起来很废力,有没有人知道中文的这样的书籍;
那位大侠不吝赐教,告诉我一本这样的书,或者这样的论坛,我找了好久也没有发现一个合意的。