C語言解讀assert函式

qq_45630711發表於2020-11-20
  • 在 C 語言中,斷言被定義為巨集的形式(assert(expression)),而不是函式,其原型定義在<assert.h>檔案中。其中,assert 將通過檢查表示式 expression 的值來決定是否需要終止執行程式。也就是說,如果表示式 expression 的值為假(即為 0),那麼它將首先向標準錯誤流 stderr 列印一條出錯資訊,然後再通過呼叫 abort 函式終止程式執行;否則,assert 無任何作用。
#include <assert.h>
void assert( int expression );
  • 預設情況下,assert 巨集只有在 Debug 版本(內部除錯版本)中才能夠起作用,而在 Release 版本(發行版本)中將被忽略。當然,也可以通過定義巨集或設定編譯器引數等形式來在任何時候啟用或者禁用斷言檢查(不建議這麼做)。同樣,在程式投入執行後,終端使用者在遇到問題時也可以重新起用斷言。這樣可以快速發現並定位軟體問題,同時對系統錯誤進行自動報警。對於在系統中隱藏很深,用其他手段極難發現的問題也可以通過斷言進行定位,從而縮短軟體問題定位時間,提高系統的可測性。
  • 已放棄使用assert()的缺點是,頻繁的呼叫會極大的影響程式的效能,增加額外的開銷。在除錯結束後,可以通過在包含#include <assert.h>的語句之前插入 #define NDEBUG 來禁用assert呼叫,示例程式碼如下:
#include <stdio.h>
#define NDEBUG
#include <assert.h>
  • 儘量在函式中使用斷言來檢查引數的合法性
    在函式中使用斷言來檢查引數的合法性是斷言最主要的應用場景之一,它主要體現在如下 3 個方面:

    1. 在程式碼執行之前或者在函式的入口處,使用斷言來檢查引數的合法性,這稱為前置條件斷言。
    2. 在程式碼執行之後或者在函式的出口處,使用斷言來檢查引數是否被正確地執行,這稱為後置條件斷言。
    3. 在程式碼執行前後或者在函式的入出口處,使用斷言來檢查引數是否發生了變化,這稱為前後不變斷言。
  • Memcpy 函式中,除了可以通過“assert(dest !=NULL&&src!=NULL);”語句在函式的入口處檢查 dest 與 src 引數是否傳入 NULL 指標之外,還可以通過“assert(tmp_dest>=tmp_src+len||tmp_src>=tmp_dest+len);”語句檢查兩個記憶體塊是否發生重疊。如下面的示例程式碼所示:

void *Memcpy(void *dest, const void *src, size_t len)
{
        assert(dest!=NULL && src!=NULL);
        char *tmp_dest = (char *)dest;
        char *tmp_src = (char *)src;
        /*檢查記憶體塊是否重疊*/
        assert(tmp_dest>=tmp_src+len||tmp_src>=tmp_dest+len);
        while(len --)
                *tmp_dest ++ = *tmp_src ++;
        return dest;
}
  • 除此之外,建議每一個 assert 巨集只檢驗一個條件,這樣做的好處就是當斷言失敗時,便於程式排錯。試想一下,如果在一個斷言中同時檢驗多個條件,當斷言失敗時,我們將很難直觀地判斷哪個條件失敗。因此,下面的斷言程式碼應該更好一些,儘管這樣顯得有些多此一舉:
assert(dest!=NULL);
assert(src!=NULL);
  • 最後,建議 assert 巨集後面的語句應該空一行,以形成邏輯和視覺上的一致感,讓程式碼有一種視覺上的美感。同時為複雜的斷言新增必要的註釋,可澄清斷言含義並減少不必要的誤用。

相關文章