「C語言筆記」assert怎麼用?

2020年02月03日12:58:08 科技 1825

一、什麼是assert()?

編寫代碼時,我們總是會做出一些假設,斷言(assert)就是用於在代碼中捕捉這些假設,可以將斷言看作是異常處理的一種高級形式。

斷言表示為一些布爾表達式,程序員相信在程序中的某個特定點該表達式值為真。可以在任何時候啟用和禁用斷言驗證,因此可以在測試時啟用斷言,而在部署時禁用斷言。同樣,程序投入運行後,最終用戶在遇到問題時可以重新啟用斷言。

注意assert()是一個宏,而不是函數。

二、assert怎麼用?

1、assert所在的頭文件及原型

在MinGW工具中,assert()宏在存在於頭文件assert.h中,其關鍵內容如下:

#ifdef NDEBUG#define assert(x)((void)0)#else /* debugging enabled */_CRTIMP void __cdecl __MINGW_NOTHROW _assert (const char*, const char*, int) __MINGW_ATTRIB_NORETURN;#define assert(e)       ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))#endif/* NDEBUG */

assert()宏接受一個整形表達式參數。如果表達式的值為假,assert()宏就會調用_assert函數在標準錯誤流中列印一條錯誤信息,並調用abort()(abort()函數的原型在stdlib.h頭文件中)函數終止程序。

當我們認為已經排除了程序的bug時,就可以把宏定義#define NDEBUG寫在包含assert.h位置前面。

小知識:

  • __cdecl是C Declaration的縮寫(declaration,聲明),表示C語言默認的函數調用方法:所有參數從右到左依次入棧。
  • _CRTIMP是C run time implement的簡寫,C運行庫的實現的意思。作為用戶代碼,不應該使用這個東西。提示是使用dll的動態 C 運行時庫還是靜態連接的 C 運行庫的一個宏。
#ifndef _CRTIMP#ifdef _DLL#define _CRTIMP __declspec(dllimport)#else /* ndef _DLL */#define _CRTIMP#endif /* _DLL */#endif /* _CRTIMP */
  • __MINGW_NOTHROW與__MINGW_ATTRIB_NORETURN是異常處理相關標識

這幾個標識符在C語言標準庫文件中都有用得到,但是我們不需要關心,在我們用戶的角度來看,以上函數原型我們看成:void _assert(const char*, const char*, int);即可。

2、assert應用

assert主要用於類型檢查及單元測試中。

單元測試(unit testing),是指對軟體中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,一般來說,要根據實際情況去判定其具體含義,如C語言中單元指一個函數。

(1)例子一:除法運算

/*編譯工具:mingw32  gcc6.3.0*/#include <stdio.h>#include <assert.h>int main(void){int a, b, c;printf("請輸入b, c的值:");scanf("%d %d", &b, &c);a = b / c;printf("a = %d", a);return 0;}

此處,變數c作為分母是不能等於0,如果我們輸入2 0,結果是什麼呢?結果是程序會蹦:

「C語言筆記」assert怎麼用? - 天天要聞

這個例子中只有幾行代碼,我們很快就可以找到程序蹦的原因就是變數c的值為0。但是,如果代碼量很大,我們還能這麼快的找到問題點嗎?

這時候,assert()就派上用場了,以上代碼中,我們可以在a = b / c;這句代碼之前加上assert(c);這句代碼用來判斷變數c的有效性。此時,再編譯運行,得到的結果為:

「C語言筆記」assert怎麼用? - 天天要聞

可見,程序蹦的同時還會在標準錯誤流中列印一條錯誤信息:

Assertion failed:c, file hello.c, line 12

這條信息包含了一些對我們查找bug很有幫助的信息:問題出在變數c,在hello.c文件的第12行。這麼一來,我們就可以迅速的定位到問題點了。

這時候細心的朋友會發現,上邊我們對assert()的介紹中,有這麼一句說明:如果表達式的值為假,assert()宏就會調用_assert函數在標準錯誤流中列印一條錯誤信息,並調用abort()(abort()函數的原型在stdlib.h頭文件中)函數終止程序。

所以,針對我們這個例子,我們的assert()宏我們也可以用以下代碼來代替:

if (0 == c){puts("c的值不能為0,請重新輸入!");abort();}

這樣,也可以給我們起到提示的作用:

「C語言筆記」assert怎麼用? - 天天要聞

但是,使用assert()至少有幾個好處:

1)能自動標識文件和出問題的行號。

2)無需要更改代碼就能開啟或關閉assert機制(開不開啟關係到程序大小的問題)。如果認為已經排除了程序的bug,就可以把下面的宏定義寫在包含assert.h的位置的前面:

#define NDEBUG

並重新編譯程序,這樣編輯器就會禁用工程文件中所有的assert()語句。如果程序又出現問題,可以移除這條#define指令(或把它注釋掉),然後重新編譯程序,這樣就可以重新啟用了assert()語句。

(2)例子二:STM32庫函數

我們來看我們比較熟悉的GPIO初始化函數:

「C語言筆記」assert怎麼用? - 天天要聞

可見,該函數的實現中,有三條assert_param()這樣的語句,其作用就是對一些函數入口參數進行一些有效性檢查。其實assert_param()這就類似與我們C標準庫中的assert()。針對stm32f10x系列來說,其被定義在文件stm32f10x_conf.h中:

「C語言筆記」assert怎麼用? - 天天要聞

這是一個例子,除了GPIO初始化函數之外,STM32固件庫函數中的其他函數都是會做這樣的參數檢查。

三、assert與if的比較?

assert()斷言功能好像用if也能實現,仔細一看這兩者還是有區別。下面看一下它們的區別:

先看一個例子,我們使用malloc函數定義一個存著堆空間中的變數,我們該怎麼定義及該怎麼做一些防禦處理呢?

首先,我們要知道,malloc函數如果分配成功內存則返回指向被分配內存的指針(此存儲區中的初始值不確定),否則返回空指針NULL。看如下代碼:

int* p = (int*)malloc(sizeof(int));assert(p);/* 錯誤示例 */

這麼寫會有問題嗎?

看似沒問題,但是問題很大!我們的assert()會在我們調試完畢之後禁用掉,這麼一來以上代碼就相當於只有下面這一句了:

int* p = (int*)malloc(sizeof(int));

此時,當我們的程序在跑的時候malloc申請不到內存空間了,也沒有做一些解決措施,可能就會產生致命錯誤。

我們應該把以上代碼改寫為:

int* p = (int*)malloc(sizeof(int));if (NULL == p) /*請使用if來判斷,這是有必要的*/{    /* 做一些處理 */}


下面看一下assert與if做防錯處理的幾點用法區別:

1、assert語句用在debug版本的調試中;if(NULL!=p)是在release版本中檢驗指針的有效性;

2、assert一般用與檢查函數參數的合法性(有效性)而不是正確性,但是合法的程序並不見得是程序邏輯正確的程序,該用if做判斷處理的地方還是得做處理。

也就是assert在調試期間用來檢查一些不允許出現的情況是否有發生,一旦發生就表明我們的程序很可能有BUG,而if判斷的就是我們理所應當處理的各種情況,且這些情況如果發生並不代表程序發生BUG。


四、_Static_assert(C11標準)

assert()是在運行時進行檢查的,如果一份工程很大,編譯起來需要很長時間,一些情況在運行時檢查,效率就比較低了。

這時候_Static_assert()就派上用場了,這是C11標準中的一個特性,_Static_assert()在編譯時進行檢查,如果編譯時檢測到代碼里的一些異常情況,就會導致程序無法通過編譯。下面來看一個例子:

/*編譯環境:mingw32  gcc6.3.0編譯命令:gcc -std=c11 hello.c -o hello.exe*/#include <stdio.h>#include <limits.h>  /*CHAR_BIT是limits.h中的一個宏*/_Static_assert(CHAR_BIT == 16, "16-bit char falsely assumed");int main(void){printf("歡迎關注嵌入式大雜燴!查看更多筆記\n");return 0;}

_Static_assert接受兩個參數,第一個參數是整型常量表達式,第二個參數是一個字元串。如果第一個表達式為0,編譯時就會輸出第二個參數的字元串,而且編譯不通過。

該程序編譯結果如下:

「C語言筆記」assert怎麼用? - 天天要聞

可見,編譯報錯了,並且列印提示了我們的問題所在點,列印出了我們_Static_assert第二個參數的字元串,這樣我們就可以很快地定位到導致編譯錯誤的問題了。

以上就是關於assert()斷言宏的一些總結筆記,如有錯誤歡迎指出!

科技分類資訊推薦

雷軍在杭州親自交付小米SU7:握手、鞠躬、送花 感謝每位車主 - 天天要聞

雷軍在杭州親自交付小米SU7:握手、鞠躬、送花 感謝每位車主

快科技5月18日消息,今天早上8:30,雷軍開啟了一場超過3小時的連續直播,駕駛小米SU7 Pro從上海世紀大道附近開往杭州。在杭州交付中心,雷軍親自為5位車主交付了SU7。雷軍還是保持了一如既往的高格局,向每一位車主鞠躬、握手,並送上花束,不斷的感謝車主。不過因為場地受限,所以並沒有像在北京工廠的首批交付上親自為車...
向「新」向好, 「星式生活」助推釋放消費活力 - 天天要聞

向「新」向好, 「星式生活」助推釋放消費活力

日前,隨著一張巨幅畫卷在上海豫園的中心廣場上展開,近百組家庭來到現場共同繪製「星式生活」畫卷,點亮「復星之星」。5月15日是國際家庭日,復星作為創新驅動的全球家庭消費產業集團,秉持讓全球家庭生活更幸福的使命,自2019年將「復星家庭日」設立在這一天,聯合生態內品牌回饋廣大用戶,通過多種年度權益日活動打造「...
「科普網紅」也能評上研究員,上海率先面向市場端開展科技傳播職稱認定 - 天天要聞

「科普網紅」也能評上研究員,上海率先面向市場端開展科技傳播職稱認定

走上2024上海科技節紅毯的「科技傳播紅人」「沒想到我能評上上海市科技傳播高級職稱。」接過證書,B站百大UP「稚暉君」、智元機器人科技有限公司創始人彭志輝驚喜地說。今天(18日)開幕的上海科技節舉行了全國首批科技傳播專業高級職稱獲得者授證儀式,除彭志輝外,還有網名「菠蘿」的科普作家李治中、「混子哥」陳磊等20...
部分用戶反饋蘋果 M4 iPad Pro 屏幕顆粒感問題 - 天天要聞

部分用戶反饋蘋果 M4 iPad Pro 屏幕顆粒感問題

IT之家 5 月 18 日消息,部分用戶在開箱體驗蘋果 M4 OLED iPad Pro 平板之後,反饋圖像看起來有顆粒感,部分用戶猜測可能是雙層串聯 OLED 技術錯位導致的。不過也有用戶反饋這個問題可能是 Mura Effect 導致的,這是所有 OLED 屏幕的通病,主要是因為亮度 / 顏色不均勻導致的。當薄膜晶體管(TFT)向 OLED 顯示屏提供不
做車和手機哪個難?雷軍解讀與華為競爭關係,點評蘋果造車失敗原因 - 天天要聞

做車和手機哪個難?雷軍解讀與華為競爭關係,點評蘋果造車失敗原因

騰訊汽車訊 5月18日消息,今日早間8:30,小米集團創始人雷軍開啟駕駛小米SU7 Pro版車型的直播,測試小米SU7的城市NOA和高速NOA表現。在談到做車和做手機哪個更難時,雷軍稱,手機行業中,目前還在牌桌上的公司能力都很強,比如蘋果、三星、華為等。雷軍坦稱與華為手機的競爭很激烈,但在內部多次強調感謝有華為這樣的高水平...
「科普網紅」也能評上研究員   上海率先面向市場端開展科技傳播職稱認定 - 天天要聞

「科普網紅」也能評上研究員 上海率先面向市場端開展科技傳播職稱認定

圖說:科普網紅「菠蘿君」(左)和「混子哥」陳磊,B站up主「稚暉君」等 新民晚報記者 陶磊 攝新民晚報訊(記者 馬亞寧)科普網紅也能評上研究員。清華本科畢業,海外博士深造,回國後一頭扎進癌症科普,成為一名自由科普人的網紅「菠蘿君」,在今天開幕的2024年上海科技節上,驚喜地獲得上海市科技傳播高級職稱。他是全國...
5G目前布局如何?多家運營商公布新進展 - 天天要聞

5G目前布局如何?多家運營商公布新進展

昨天,在寧波舉行的世界電信和信息社會日大會上,中國移動、中國電信、中國聯通、中國廣電共同啟動了5G異網漫遊商用推廣。  我國啟動5G異網漫遊商用推廣  5G異網漫遊是指用戶所屬運營商無5G網路覆蓋時可接入其他運營商的5G網路,繼續使用5G服務。用戶在開通5G異網漫遊的區域,使用支持5G異網漫遊的終端,不用換卡換號就...
四川聯通重磅發布「家庭寬頻全光普及行動」——讓全光寬頻走進千家萬戶 - 天天要聞

四川聯通重磅發布「家庭寬頻全光普及行動」——讓全光寬頻走進千家萬戶

5月17日,四川聯通寬頻新變革發布會暨四川聯通第6屆國企開放日在成都召開。會上,四川聯通發布「家庭寬頻全光普及行動」並舉行啟動儀式,助力四川家庭加速步入全屋千兆光網的智慧家庭新生活。國家「十四五」規劃綱要明確提出,要加快5G網路規模化部署,推廣升級千兆光纖網路。根據今年4月工信部公布的2024年一季度我國通信...
蘋果新規被指設門檻:開發者測試第三方瀏覽器引擎僅限歐洲設備 - 天天要聞

蘋果新規被指設門檻:開發者測試第三方瀏覽器引擎僅限歐洲設備

IT之家 5 月 18 日消息,科技媒體 The Register 近日報告稱,蘋果公司固然開放了第三方瀏覽器引擎,但實際推行過程中對開發者並不友好,蘋果會限制測試範圍僅限歐盟國家的設備。報告認為其它國家和地區的開發者如果想要開拓歐洲市場,開發和支持採用第三方網路引擎的瀏覽器或者相關 App,那麼蘋果的這項限制增加了很多額外...