天天看點

你真的會使用assert嗎?

寫這篇部落格源于在閱讀lighttpd源代碼是遇到的一個關于assert應用的疑問。

在閱讀lighttpd源代碼時,發現比比皆是的對malloc的調用結果進行assert檢查,比如:Buffer.c:

你真的會使用assert嗎?

buffer* buffer_init(void) {
    buffer *b;

    b = malloc(sizeof(*b));
    assert(b);

    b->ptr = NULL;
    b->size = 0;
    b->used = 0;

    return b;
}      
你真的會使用assert嗎?

這裡的assert(b)似乎有問題,實際release版本在運作中難道不會發生malloc傳回NULL的情況嗎?之後在閱讀

《Writing Solid Code 》

一書時找到了答案。

對assert的基本用法就不再累述了,下面總結一下assert的實際應用的Recommended practice吧:

1、要使用斷言對函數參數進行确認 

主要有以下情況:

  • 指針不是NULL的斷言;
  • index值或size值不是負值或小于已知限值的斷言;這一條也可以這麼描述:要從程式中删去無定義的特性或者在程式中使用斷言來檢查出無定義特性的非法使用

2、每個斷言必須在頭檔案中的函數功能描述的斷言部分進行說明(不要浪費别人的時間 ─── 詳細說明不清楚的斷言 ),例如:

*  Asserts:
 *      'size' is no greater then LIMIT.
 *      'format' is not NULL.
 *      The function result is no greater than LIMIT.
 */      

如果沒有斷言, 寫 “Nothing”:

*  Asserts:
 *      Nothing
 */      
(以上的格式也許嚴格了一些,不過如果真的這麼做,對代碼的可閱讀性會很有幫助)      

3、斷言和錯誤校驗的差別

正确使用斷言,必須要清楚程式錯誤(program errors)和運作時錯誤(run- time errors)之間的差別; 

  1. 一個程式錯誤是一個bug,永遠不應該發生。
  2. 一個運作時錯誤是在程式運作的任何時候都可能會發生.

斷言并不是一種處理運作時錯誤的機制。例如在需要輸入正數的時候,使用者輸入了一個負數,如果用斷言來檢測這種情況就不是好的設計。對于這種情況需要用合适的錯誤檢查和恢複處理的代碼來進行處理。

再回到lighttpd中對malloc函數的傳回值進行assert斷言,我覺得也屬于這個問題,這應該是一個運作時錯誤,而不是程式錯誤;是以,我覺得《C和指針》一書中對malloc傳回NULL處理是通過一個錯誤檢查配置設定器來處理的。

4、斷言和bug

斷言大緻分為前置條件(Preconditions)、後置條件(Postconditions)、不變性條件(Invariants)

如果前置條件不成立,發生Assertion violations,則調用該函數的代碼存在bug,需要盡快找到并解決;

如果後置條件不成立,發生Assertion violations,則(函數的)實作代碼存在bug,需要盡快找到并解決;

例如:

你真的會使用assert嗎?
void doBlah(int x)

{

    assert(x!=0);

    ....

}       
你真的會使用assert嗎?

這段代碼說明這個函數的調用不可能傳入參數0,如果發生這種情況,說明調用這個函數的代碼存在bug;

以上是自己的一點了解,歡迎高手指正!!!

參考:

How to use assertions in C

        《

Writing Solid Code