assert用法,原理,改編(C++)

最近才發現,原來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)

套用上面的寫法,既然是爲真發警告,那麼用&&去換||就行了。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章