0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

【C語(yǔ)言進(jìn)階】利用assert高效排查你的C程序

嵌入式物聯(lián)網(wǎng)開(kāi)發(fā) ? 來(lái)源:嵌入式物聯(lián)網(wǎng)開(kāi)發(fā) ? 作者:嵌入式物聯(lián)網(wǎng)開(kāi)發(fā) ? 2022-08-31 13:27 ? 次閱讀
眾所周知,我們?cè)趯?shí)際開(kāi)發(fā)C程序的時(shí)候,往往是編碼容易——調(diào)試?yán)щy,修改容易——排查困難。我們?cè)陂_(kāi)發(fā)過(guò)程中,debug占據(jù)了我們很大一部分的時(shí)間,而正確地使用各種編碼手段,可以有效地提升排查問(wèn)題代碼的效率。筆者從自己的實(shí)踐經(jīng)驗(yàn)出發(fā),給大家分享一個(gè)用于編碼/調(diào)試階段高效發(fā)現(xiàn)問(wèn)題代碼的利器,這就是大名鼎鼎的**assert**。通過(guò)閱讀本文,你將了解到以下內(nèi)容:
  • 什么是assert?
  • assert有什么用?
  • assert怎么使用?
  • assert的常規(guī)操作有哪些?

什么是assert?


? assert它的中文含義是“斷言”,它被包含在中,往往給使用者呈現(xiàn)的形式為: assert() 。因此,很多開(kāi)發(fā)者認(rèn)為它就是一個(gè)函數(shù),可能它的原型就是void assert(int expression); 但研究過(guò)assert.h的,一定會(huì)發(fā)現(xiàn),其實(shí)并不是。

? assert的真身,其實(shí)是一個(gè)宏定義,只不過(guò)是一個(gè)帶參數(shù)輸入的宏定義,與我們之前一篇八卦Linux內(nèi)核設(shè)計(jì)的max宏定義 (【Linux內(nèi)核】從小小的宏定義窺探Linux內(nèi)核的精妙設(shè)計(jì))類(lèi)似的。廬山真面目如下所示:

#define assert(e) ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-zc6EAp8U-1661923571346)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 從它的定義,我們可以很清晰的知道,真正起到打印作用的是_assert,而它才是真正的一個(gè)函數(shù)。原型為:

void _assert(const char *e, const char *file, int line);

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-dax1ldZf-1661923571352)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]


assert有什么用?


? 本文的主題是利用assert高效排查問(wèn)題代碼,自然assert的用途就是排查代碼;但是,具體它的功能是怎么體現(xiàn)呢?假設(shè)有如下代碼,一個(gè)測(cè)試函數(shù)的實(shí)現(xiàn)片段:

int test_function(int a, int *b)
{
    assert(a > 1);  /* 斷言:入?yún)的值一定大于1 */
    assert(b);      /* 斷言: 入?yún)指針一定不是NULL */

    /* Do other things here ... */
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-ejym7Jij-1661923571353)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 如代碼所示,有一個(gè)測(cè)試函數(shù)test_function,接收2個(gè)入?yún)ⅲ粋€(gè)是int型的a變量,一個(gè)int *類(lèi)型的b指針;在函數(shù)的開(kāi)始,我們就用assert分別對(duì)a和b做了斷言,確保它們有正確的輸入。假設(shè)我們有如下的函數(shù)調(diào)用的測(cè)試代碼:

{
    int a = 7;
    int *b = &a;
    
    test_function(a, b);

    /* Do other things here ... */
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-E4QQ143R-1661923571355)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 很明顯,當(dāng)如上代碼調(diào)用test_fucntion時(shí),內(nèi)部的兩個(gè)assert判斷均為【真】,那么什么事情也不會(huì)發(fā)生,assert就像一個(gè)優(yōu)雅的淑女,靜靜地站在那里看著你。

? 當(dāng)我們的測(cè)試代碼做如下調(diào)整:

{
    int a = 0;
    int *b = &a;
    
    test_function(a, b);

    /* Do other things here ... */
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-qPbDKDBb-1661923571357)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 很明顯,test_function的第一個(gè)assert語(yǔ)句不為【真】,那么它就像山洪一樣要爆發(fā)了,終止程序運(yùn)行的同時(shí),會(huì)輸出類(lèi)似的錯(cuò)誤提示: Assertion failed: a > 1,file xxx.c, line 128,這段錯(cuò)誤提示,不僅告訴了我們哪個(gè)條件判斷出錯(cuò)了,并且還告訴了我們出錯(cuò)的位置在哪個(gè)文件的哪一行,這是多么智能?。∮纱丝梢?jiàn),它真正的威力在于【代碼出錯(cuò)】時(shí),即當(dāng)代碼沒(méi)有按照我們的斷言進(jìn)行時(shí),我們就應(yīng)該停下來(lái),排查下為何會(huì)有錯(cuò)誤的參數(shù)輸入,這樣我們就可以將bug在出現(xiàn)苗頭的時(shí)候就把它消滅掉。


assert怎么使用?


? 其實(shí),上面的示例代碼已經(jīng)展示了如何使用assert,但是我們需要補(bǔ)充的是,一般在使用assert斷言語(yǔ)句的時(shí)候,需要在對(duì)應(yīng)的.c文件加上對(duì)assert.h的引用,否則編譯會(huì)報(bào)錯(cuò)誤。

? assert這么智能的利器是非常有利于我們寫(xiě)出高質(zhì)量不易出錯(cuò)的代碼的,通常富有經(jīng)驗(yàn)的程序員都會(huì)很擅長(zhǎng)使用assert語(yǔ)句,把a(bǔ)ssert打在恰當(dāng)?shù)恼Z(yǔ)句中,可以最大限度地提升我們的代碼質(zhì)量。但是,很多開(kāi)發(fā)者開(kāi)始有疑惑了,要是每條語(yǔ)句,每個(gè)判斷都加上assert,那么就算全部assert的情況都是【真】,也夠CPU忙一會(huì)了,這樣似乎有些浪費(fèi)CPU的計(jì)算能力,以追求高效的C語(yǔ)言編程,可容不下這樣的事情發(fā)生。那,這可怎么辦呢?

? 為避免以上情況的發(fā)生,我們作為assert的使用者,一般只需要在開(kāi)發(fā)調(diào)試階段才使用assert,而在正式發(fā)布的版本是需要去掉assert的。這樣疑惑就更大了,發(fā)布版本一條條刪掉assert調(diào)用,萬(wàn)一刪錯(cuò)了代碼呢?設(shè)計(jì)者總是聰明的,他們也早就想到了這一點(diǎn),這不他們也提供了解決方案。開(kāi)頭的時(shí)候,我們介紹了assert是一個(gè)宏,但并沒(méi)有完全展示它的全貌?,F(xiàn)在開(kāi)始展示它的真容:

#ifdef NDEBUG
#define assert(e) (void)0
#else
#define assert(e) ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))
#endif

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-69cPczpO-1661923571359)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 聰明的你,一定也發(fā)現(xiàn)了,我們只需要在.c文件#include 之前,加上一句#define NDEBUG 1就可以把相應(yīng).c中的assert(e)全部變成((void)0);而((void)0)本身是個(gè)無(wú)效調(diào)用代碼,在實(shí)際的編譯過(guò)程中是會(huì)被優(yōu)化掉的,這樣我們僅增加對(duì)NDEBUG(NO DEBUG的意思)的宏定義,就可以把全部的assert給摒棄了,是不是很智能呢?


assert的常規(guī)操作有哪些?


? 正如上面所述,assert這么智能,但是我們也不能濫用,只需要在恰當(dāng)?shù)奈恢米鳛樘囟ǖ呐袛?;通常?lái)說(shuō),我們有以下一些情景可以考慮使用assert語(yǔ)句:

  • 函數(shù)的入?yún)⑴袛?,?duì)錯(cuò)誤的入?yún)⒓皶r(shí)處理
  • 對(duì)重點(diǎn)調(diào)用的系統(tǒng)函數(shù)的返回結(jié)果做判斷,使用assert保證系統(tǒng)調(diào)用的結(jié)果是正確的,避免外部使用不正確的系統(tǒng)調(diào)用而出現(xiàn)錯(cuò)上加錯(cuò)的情況;
  • switch語(yǔ)句中,如果不允許出現(xiàn)default的情況,可以考慮在default分支中加入assert(0);
  • 執(zhí)行計(jì)算時(shí),做計(jì)算的輸入或計(jì)算結(jié)果的輸出等做下判斷,比如除數(shù)不能為0,比如一個(gè)百分比值不能超過(guò)100%等等。

? 綜述,assert是把雙刃劍,出錯(cuò)時(shí)它能很優(yōu)秀地暴露問(wèn)題代碼,非常有利于我們排查代碼,從而以最快的速度找到問(wèn)題并解決問(wèn)題;同時(shí),它的頻繁調(diào)用,一定程度上加上了CPU的處理,做一些無(wú)畏的判斷,“簡(jiǎn)直就是在浪費(fèi)生命”。所以,在實(shí)際開(kāi)發(fā)過(guò)程中,我們務(wù)必要嚴(yán)謹(jǐn)細(xì)致地使用assert,讓它更好地為我們服務(wù)。

? 只有不自負(fù)且思維嚴(yán)謹(jǐn)?shù)娜瞬拍苁褂煤胊ssert,我們只有做到了不自負(fù),不對(duì)自己的代碼打100%的包票,相信是代碼總會(huì)有出錯(cuò)的時(shí)候,才會(huì)逐步養(yǎng)成思維嚴(yán)謹(jǐn)?shù)牧?xí)慣,反而對(duì)自己的代碼質(zhì)量有更大的提升。

? 本文對(duì)assert的介紹和使用做了一番總結(jié),文中難免有紕漏之處,還望讀者誠(chéng)心指正,感謝。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7594

    瀏覽量

    135856
  • C程序
    +關(guān)注

    關(guān)注

    4

    文章

    254

    瀏覽量

    35967
  • ASSERT
    +關(guān)注

    關(guān)注

    0

    文章

    17

    瀏覽量

    7221
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C語(yǔ)言assert的使用

    assert意思是斷言,常用在程序的DEBUG版本中。
    發(fā)表于 07-21 14:51 ?790次閱讀

    C語(yǔ)言assert(斷言)簡(jiǎn)介

    assert的功能,條件為真,程序繼續(xù)執(zhí)行;如果斷言為假(false),則程序終止。
    的頭像 發(fā)表于 11-17 16:33 ?1095次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b><b class='flag-5'>assert</b>(斷言)簡(jiǎn)介

    C語(yǔ)言進(jìn)階

    C語(yǔ)言進(jìn)階見(jiàn)附件
    發(fā)表于 08-13 15:51

    C語(yǔ)言進(jìn)階書(shū)分享!

    挺好的。c語(yǔ)言進(jìn)階.pdf (1.78 MB )
    發(fā)表于 10-16 02:44

    100個(gè)經(jīng)典C語(yǔ)言程序

    c語(yǔ)言編寫(xiě),c語(yǔ)言的100個(gè)經(jīng)典程序,單片機(jī)的應(yīng)用,開(kāi)發(fā)利用
    發(fā)表于 12-17 11:46 ?11次下載

    時(shí)鐘設(shè)計(jì)程序C語(yǔ)言

    時(shí)鐘設(shè)計(jì)程序。時(shí)鐘設(shè)計(jì)程序。時(shí)鐘設(shè)計(jì)程序C語(yǔ)言】時(shí)鐘設(shè)計(jì)程序
    發(fā)表于 12-28 12:02 ?0次下載

    單片機(jī)IO擴(kuò)展(進(jìn)階)程序集合【C語(yǔ)言+匯編】

    單片機(jī)IO擴(kuò)展(進(jìn)階)程序集合【C語(yǔ)言+匯編】
    發(fā)表于 01-06 11:03 ?8次下載

    單片機(jī)IO擴(kuò)展(進(jìn)階)程序集合【C語(yǔ)言

    單片機(jī)IO擴(kuò)展(進(jìn)階)程序集合【C語(yǔ)言】。
    發(fā)表于 01-06 11:04 ?23次下載

    DSP C2000程序員高手進(jìn)階

    DSP C2000程序員高手進(jìn)階 PDF 版
    發(fā)表于 05-06 15:13 ?33次下載

    DSP C2000程序員的高手進(jìn)階

    DSP C2000程序員的高手進(jìn)階
    發(fā)表于 10-16 13:16 ?20次下載
    DSP <b class='flag-5'>C</b>2000<b class='flag-5'>程序</b>員的高手<b class='flag-5'>進(jìn)階</b>

    C語(yǔ)言進(jìn)階學(xué)習(xí)課件資料合集

    本文檔的主要內(nèi)容詳細(xì)介紹的是C語(yǔ)言進(jìn)階學(xué)習(xí)課件資料合集包括了:第1節(jié)-數(shù)據(jù)的存儲(chǔ),第2節(jié)-指針的進(jìn)階,第3節(jié)-字符串+內(nèi)存函數(shù)的介紹,第4節(jié)-自定義類(lèi)型詳解(結(jié)構(gòu)體+枚舉+聯(lián)合),第
    發(fā)表于 07-14 08:00 ?11次下載
    <b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>的<b class='flag-5'>進(jìn)階</b>學(xué)習(xí)課件資料合集

    C語(yǔ)言進(jìn)階】sprintf和snprintf的區(qū)別

    C語(yǔ)言進(jìn)階】sprintf 和 snprintf 真的沒(méi)有區(qū)別嗎?
    的頭像 發(fā)表于 08-31 13:18 ?1.2w次閱讀

    C語(yǔ)言進(jìn)階C語(yǔ)言指針的高階用法

    C語(yǔ)言進(jìn)階C語(yǔ)言指針的高階用法
    的頭像 發(fā)表于 08-31 13:24 ?2247次閱讀

    C語(yǔ)言進(jìn)階之嵌入式系統(tǒng)高級(jí)C語(yǔ)言編程

    電子發(fā)燒友網(wǎng)站提供《C語(yǔ)言進(jìn)階之嵌入式系統(tǒng)高級(jí)C語(yǔ)言編程.rar》資料免費(fèi)下載
    發(fā)表于 11-18 10:32 ?1次下載
    <b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b><b class='flag-5'>進(jìn)階</b>之嵌入式系統(tǒng)高級(jí)<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>編程

    C語(yǔ)言構(gòu)建高效的嵌入式程序

    嵌入式工程師在編寫(xiě)C語(yǔ)言程序時(shí),需要注重效率和清晰的思路。本文將通過(guò)解析經(jīng)典問(wèn)題“猴子選大王”來(lái)展示如何用C語(yǔ)言思維方式構(gòu)建
    的頭像 發(fā)表于 12-21 09:27 ?584次閱讀