按照字母序首先我們來看看<assert.h>,這個(gè)文件提供的接口功能很簡(jiǎn)單,但卻是我們極其常用的功能-斷言機(jī)制(如果條件為False,則輸出Diagnostics信息,然后Abort)。當(dāng)然在最終產(chǎn)品中使用斷言并不是一種好的方法,不過斷言是一種很有用的幫助我們調(diào)試程序的好工具。 我們一般在程序的調(diào)試版本中使用斷言機(jī)制,一般用來對(duì)Input進(jìn)行Validation,輸出一些Diagnostics信息。如: assert((idx > 10) && (idx < 100)); <assert.h>中提供一個(gè)宏assert,這個(gè)宏的功能由另一個(gè)宏NDEBUG(標(biāo)志是否是DEBUG版本)決定。如果NDEBUG宏在你include <assert.h>時(shí)沒有被定義,這時(shí)斷言功能開啟;否則斷言功能關(guān)閉。如: #define NDEBUG #include <assert.h> /* 此時(shí)斷言功能關(guān)閉 */ 你也大可不必在你的各個(gè)源文件中控制斷言功能的開關(guān),在編譯器選項(xiàng)中同樣可以定義NDEBUG宏,如gcc -DNDEBUG test.c,當(dāng)然對(duì)于大的project,這些是應(yīng)該放在Makefile中的,這樣的結(jié)果就相當(dāng)于在你所有#include <assert.h>的地方之前定義了NDEBUG宏,也就是說在每個(gè)編譯單元中,斷言功能都是關(guān)閉的。 assert宏看起來很簡(jiǎn)單,但是由于其是C標(biāo)準(zhǔn)庫(kù)提供的接口,所以在實(shí)現(xiàn)的時(shí)候需要考慮的更加細(xì)致和全面一些。從上面的敘述上來看assert.h文件的結(jié)構(gòu)應(yīng)該大致如下: #undef assert #ifdef NDEBUG #define assert(test) ((void)0) #else #define assert(test) ... #endif 我們可以很輕松的就拿出一個(gè)assert的實(shí)現(xiàn)版本: /* NDEBUG not defined */ #define assert(test) if (!(test)) \ fprintf(stderr, "Assertion Failed: %s, file %s, line %d\n", \ #test, __FILE__, __LINE__); \ 那么這個(gè)版本的實(shí)現(xiàn)可以接受不,答案是不能。原因有以下幾點(diǎn): 1) 這個(gè)實(shí)現(xiàn)中直接用到了stderr和fprintf,這兩個(gè)符號(hào)都是在<stdio.h>中聲明的,但是C標(biāo)準(zhǔn)庫(kù)頭文件基本上都是各自獨(dú)立的,在<assert.h>中是不會(huì)再包含其他頭文件的,那么這就要求使用assert的程序自己包含<stdio.h>,這顯然不符合一個(gè)C標(biāo)準(zhǔn)庫(kù)的基本要求; 2) assert宏應(yīng)該最終展開為一個(gè)void expression,因?yàn)橛脩艉芸赡茉谒麄兊某绦蛑袑懗鱿?assert(0 < x), x < y)這樣的代碼來,而在上面的實(shí)現(xiàn)版本中,顯然assert展開后不是一個(gè)void expression。 我們?cè)賮砜纯碢.J.Plauger的實(shí)現(xiàn)版本: /* NDEBUG not defined */ void _Assert(char *); #define _STR(x) _VAL(x) #define _VAL(x) #x #define assert(test) (test) ? (void)0 \ : _Assert(__FILE__ ":" _STR(__LINE__) " " #test) /* in xassert.c */ #include <assert.h> #include <stdio.h> void _Assert(char *msg) { fprintf(stderr, "%s -- assertion failed\n", msg); abort(); } 分析一下這一版本的實(shí)現(xiàn),首先assert宏并沒有直接調(diào)用任何庫(kù)輸出函數(shù),而是調(diào)用了一個(gè)自己實(shí)現(xiàn)的函數(shù)_Assert,把向stderr輸出診斷信息的活都交給了_Assert。_STR和_VAL是兩個(gè)輔助宏,用來將__LINE__字符串化。這里比較難懂的地方就是_Assert(__FILE__ ":" _STR(__LINE__) " " #test)這一句,其實(shí)這個(gè)也很好理解??纯聪旅嬲Z(yǔ)句的執(zhí)行結(jié)果: printf("%s\n", "Hello" " " "Tony!"); 執(zhí)行上面語(yǔ)句你會(huì)看到Hello Tony!,這樣一來實(shí)際上_Assert(__FILE__ ":" _STR(__LINE__) " " #test)就可以被理解為: _Assert("THE_FILENAME_STRING" ":" "THE_LINE_STRING" " " "THE_TEST_STRING")
同意,只是存在這么一個(gè)宏,但是這個(gè)具體的值是不重要的,就可以把它忽略
|
|