最近才發現,原來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)
套用上面的寫法,既然是爲真發警告,那麼用&&去換||就行了。