c++ assert()學習總結

ASSERT只有在Debug版本中才有效,如果編譯爲Release版本則被忽略。

assert宏的原型定義在<assert.h>中,其作用是如果它的條件返回錯誤,則終止程序執行,原型定義:
#include <assert.h>
void assert( int expression );

assert的作用是計算表達式expression ,如果其值爲假(即爲0),那麼它先向stderr打印一條出錯信息,然後通過調用 abort 來終止程序運行。

使用assert的缺點是,頻繁的調用會極大的影響程序的性能,增加額外的開銷。

在調試結束後,可以通過在包含#include <assert.h>的語句之前插入#define NDEBUG 來禁用assert調用,示例代碼如下:
#include <stdio.h>
#define NDEBUG
#include <assert.h>


用法總結與注意事項:

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)的使用,方法很簡單。爲什麼要用,初學者可能比較迷惑。   
契約式編程講的比較清楚,建議可以先看看這類書。   
一個函數由前置條件、後置條件和不變式組成。在VC中,我們可以通過斷言來保證這三個條件。可以大大提高了軟件的質量。   
---------------------------------------------------------------   

如果ASSERT()中的條件不成立(比如ASSERT(0);),會彈出一個比較嚇人的對話框。
點擊重試,可以到達ASSERT斷言不成立的那一行,此時可以在watch窗口查看變量值,找出出錯的原因。


提高程序健壯性之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 &lt;= 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);,這將幫助你檢測函數的錯誤使用,這也給源代碼閱讀者很清晰的印象,那就是在這裏對函數的參數值有限制。

說了這麼多,行動起來吧,大膽的在你的程序中使用斷言。



發佈了57 篇原創文章 · 獲贊 29 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章