請看下面的程序清單badptr.c:php
#includeexpress
#include編程
#includeapp
int main( void )函數
{性能
FILE *fp;測試
fp = fopen( "test.txt", "w" );//以可寫的方式打開一個文件,若是不存在就建立一個同名文件ui
assert( fp ); //因此這裏不會出錯this
fclose( fp );編碼
fp = fopen( "noexitfile.txt", "r" );//以只讀的方式打開一個文件,若是不存在就打開文件失敗
assert( fp ); //因此這裏出錯
fclose( fp ); //程序永遠都執行不到這裏來
return 0;
}
[root@localhost error_process]# gcc badptr.c
[root@localhost error_process]# ./a.out
a.out: badptr.c:14: main: Assertion `fp' failed.
已放棄
使用assert的缺點是,頻繁的調用會極大的影響程序的性能,增長額外的開銷。
在調試結束後,能夠經過在包含#include的語句以前插入 #define NDEBUG 來禁用assert調用,示例代碼以下:
#include
#define NDEBUG
#include
用法總結與注意事項:
1)在函數開始處檢驗傳入參數的合法性
如:
int resetBufferSize(int nNewSize)
{
//功能:改變緩衝區大小,
//參數:nNewSize 緩衝區新長度
//返回值:緩衝區當前長度
//說明:保持原信息內容不變 nNewSize<=0表示清除緩衝區
assert(nNewSize >= 0);
assert(nNewSize <= MAX_BUFFER_SIZE);
...
}
2)每一個assert只檢驗一個條件,由於同時檢驗多個條件時,若是斷言失敗,沒法直觀的判斷是哪一個條件失敗
很差: assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);
好: assert(nOffset >= 0);
assert(nOffset+nSize <= m_nInfomationSize);
3)不能使用改變環境的語句,由於assert只在DEBUG個生效,若是這麼作,會使用程序在真正運行時遇到問題
錯誤: assert(i++ < 100)
這是由於若是出錯,好比在執行以前i=100,那麼這條語句就不會執行,那麼i++這條命令就沒有執行。
正確: assert(i < 100)
i++;
4)assert和後面的語句應空一行,以造成邏輯和視覺上的一致感
5)有的地方,assert不能代替條件過濾。
編寫能正常運行的程序很難;編寫在錯誤狀況下仍然表現的很「優雅」的程序更難。這篇文章將和你們討論一些編程技巧,可使咱們在運行中的程序中早點發現錯誤,檢測和從問題中恢復。那就先討論下斷言(assert)的使用吧。
在編碼時,有一個好的目標應該時刻銘記在心,那就是:應該想辦法讓bug或者異常錯誤儘早使得程序down掉,或者出現錯誤。由於這樣能夠幫助你在開發和測試階段儘快找出bug。有一些錯誤不會平白無故的暴露本身,每每是產品都到了客戶手上,這些錯誤纔會顯現出來。
一個最簡單的檢查異常條件的方法是使用標準C的assert宏,它的參數是一個bool表達式。當表達式爲假時,程序會退出。在退出以前打印錯誤消息,包括源文件,行號,和表達式自己。斷言很是有用,它提供了一個做用於程序內部的普遍的一致性檢查方法。例如,使用斷言測試函數參數的有效性,測試異常的返回值等等。
每個斷言的使用不只提供了一個程序運行時的條件檢查,也像一個對源代碼級別的程序操做的說明性文檔。若是你的程序包含了一個斷言,也就是告訴那些閱讀你源代碼的人,在你的源代碼中,在程序的這一點,這個條件應該爲真,若是不爲真,那就是一個bug。
固然,在追求性能的代碼中,使用assert會下降程序性能。可是你放心,在編譯時加入NDEBUG參數編譯器就能夠對assert進行預處理,從而移除它。正由於在預處理時可能移除assert,那你使用時就得當心了。何時用,何時不用就成了一個問題。一般,你不該該在assert內部調用函數,定義變量,或者使用改變值的操做符,如++。
咱們假設你這樣使用了:
for (i = 0; i <= 100; ++i) assert (do_something () == 0);
而後,你可能會發現這樣會使得性能大大下降,從而在創新編譯使使用NDEGUG參數。這將移除整個assert宏,這就將do_something( )也被移除了,不再被調用。爲了糾正錯誤,你應該這樣寫:
for (i = 0; i <= 100; ++i) { int status = do_something (); assert (status == 0); }
另外應該銘記在心的是,不要用assert去檢查無效的輸入。用戶可不喜歡本身在輸入時程序直接退出,即使是輸入錯誤,程序最好也有友好的響應。因此,你應該對無效輸入進行檢查,並輸出一些有用的提示信息。只在程序運行中進行內部檢查時使用斷言。
在這裏,我會給出一些比較好的在程序中使用assert的地方:
(1)空指針檢查。例如,針對一個函數的參數進行空指針檢查。你能夠這樣使用:assert (pointer != NULL);,產生的錯誤會像這樣:Assertion ‘pointer != ((void *)0)’ failed。這樣,當出現空指針時,你的程序就會退出,並很好的給出錯誤信息。
(2)檢查函數參數的值。例如,若是一個函數只能在它的一個參數foo爲正值的時候被調用,你能夠在函數開始時這樣寫:assert (foo > 0);,這將幫助你檢測函數的錯誤使用,這也給源代碼閱讀者很清晰的印象,那就是在這裏對函數的參數值有限制。
說了這麼多,行動起來吧,大膽的在你的程序中使用斷言。