轉自: http://hi.baidu.com/hplonline/blog/item/8637ab4470ee268bb3b7dcaa.html
最近才發現,原來assert這麼好用啊。。。
再看看是怎麼實現的,又找到了些有趣的東西。
用法:
先包含
#inlcude <assert.h>
在想用的地方給一句:
assert(expression)就可以了。
expression是任意有效邏輯運算式。
比如:
FILE *fp = fopen("in.txt","r") ;
if ( ! fp ){
exit(0) ;
}
assert(fp != NULL) ;
當expression不滿足時,就會報出一個很醜陋的框框,
然後向控制台輸出assert不滿足的檔案和行號。
具體到debug的時候,
可以撒網式地在各個地方放上認為應該為真的運算式的assert,
說不定哪個就爆了,於是趁機發現了問題。
原理:
只要有源碼就沒有秘密,
所以開啟assert.h,看看裡面是怎麼寫的。
主要的就這兩句:
_CRTIMP void __cdecl _assert(void *, void *, unsigned);
#define assert(exp) (void)( (exp) || (_assert(#exp, __FILE__, __LINE__), 0) )
第一句就乾的就是輸出一些資訊,然後彈出個框框,
順便結束程式這些勾當。
他被調用的時候,是類似於:
_assert("false" , "c:\\1.cpp" , 15)
這樣。
第二句的構造可謂精簡啊,小小一句話還包含了挺多以前沒注意到的事情。
1.短路求值
這個是c的重要特性,在處理&&的時候前面為假則不用繼續,
在處理|| 的時候,前面為真則不用繼續。
形象地說把後面的運算式短路了。
2.單行宏
#exp 產生"exp"這樣的字串
#@a 產生'a'這樣的字元
a##b 把a和b串連起來
第一個用法在這裡見到了,第二個暫時還沒見到用的執行個體。
第三個在a和b是宏的參數的時候有用。否則直接的ab會被當作一個東西。
3.特殊的預定義宏
__FILE__ 會被替換成所在的檔案,字串形式
__LINE__ 會被替換成行號,unsigned類型
__DATE__ 會被替換成日期
__TIME__ 會被替換成時間
其實之前翻過的跟C有關的書應該都講了這些的。
不過拿著一個列表,又不給出真正實用的例子,
當然不知道這些東西是怎麼回事,
久了自然也就忘了。
4.逗號運算式
感覺實在是一個用的很少的事情,
畢竟有多句話的時候,完全可以用分號就行了。
雖然有好多地方在if之類的裡面很壓縮的用逗號運算式寫好幾句話,
其實都可以改得不用逗號運算式的。
其一是逗號運算式的優先順序很低,所以後面那對括弧實在是不可缺少。
其二是逗號運算式的值為最右邊式子的值。
這個估計很多人都記過,但不見得有啥重大意義。
這裡,倒確實是發揮了他的意義。
因為_asert這個函數是void型的,
如果不使用逗號運算式在右邊補個0的話,
會報告:(VC6)
error C2297: '||' : illegal, right operand has type 'void'
改編:
知道是怎麼回事,當然可以很容易做出自己想要的東西。
再說還有asert.h裡面的參照呢。
比如,我嫌預設的_assert彈出的東西看著太壓抑了。。。
就自己寫個就行了。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_BUFFER 200
void _assert(char *msg , char *file , unsigned line){
char buf[MAX_BUFFER] ;
sprintf(buf , "assertion fail:\n%s\nin file:\n%s\non line:\n%d" , msg , file , line) ;
::MessageBox(NULL , buf , "assertion failure" , MB_OK) ;
exit(0) ;
}
#define assert(exp) ((exp) || (_assert(#exp , __FILE__ , __LINE__) , 0) )
int main(){
assert(1 == 1 && 3 == 4) ;
return 0 ;
}
效果:
至於我的這個是不是更壓抑。。那不屬於這裡討論的問題了。
反正通過簡單變更_assert函數,可以把相關情況輸出到檔案,
或者選擇另外的方式表達出來,能想到的都可以。
至於assert這個宏,也有可以動手腳的地方。
內建的是assert一個為真的運算式。
有的時候就想assert一個為假的運算式,當他為真的時候發出警告。
比如:
FILE *fp = fopen("in.txt","r") ;
if ( ! fp ){
exit(0) ;
}
warn(fp == NULL)
套用上面的寫法,既然是為真發警告,那麼用&&去換||就行了。