我記得在以前的一篇隨筆中,我堆windows下的<assert.h>進行了分析,今天我們來看看gcc中這個檔案的定義是怎樣的。
【1】assert宏的作用
assert宏實現斷言的作用,一般在源檔案中引用格式如下:
#include <assert.h>#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
View Code
【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 <stdio.h>/* 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 <crtdefs.h>#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, 是英語版的,看起來很廢力,有沒有人知道中文的這樣的書籍;
那位大俠不吝賜教,告訴我一本這樣的書,或者這樣的論壇,我找了好久也沒有發現一個合意的。