do{...}while(0)的妙用

1.幫助定義復(fù)雜的宏以避免錯(cuò)誤

舉例來(lái)說(shuō)藐守,假設(shè)你需要定義這樣一個(gè)宏:
#define DOSOMETHING() foo1(); foo2();
這個(gè)宏的本意是赦政,當(dāng)調(diào)用DOSOMETHING()時(shí)撬讽,函數(shù)foo1()和foo2()都會(huì)被調(diào)用铲觉。但是如果你在調(diào)用的時(shí)候這么寫:

if(a>0)
    DOSOMETHING();

因?yàn)楹暝陬A(yù)處理的時(shí)候會(huì)直接被展開窝稿,你實(shí)際上寫的代碼是這個(gè)樣子的:

if(a>0)
    foo1();
    foo2();

這就出現(xiàn)了問(wèn)題,因?yàn)闊o(wú)論a是否大于0访忿,foo2()都會(huì)被執(zhí)行瞧栗,導(dǎo)致程序出錯(cuò)。
那么僅僅使用{}將foo1()和foo2()包起來(lái)行么海铆?比如:
#define DOSOMETHING() { foo1(); foo2(); }
我們?cè)趯懘a的時(shí)候都習(xí)慣在語(yǔ)句右面加上分號(hào)迹恐,如果在宏中使用{},代碼編譯展開后宏就相當(dāng)于這樣寫了:“{...};”卧斟,展開后就是這個(gè)樣子:

if(a>0)
{
    foo1();
    foo2();
};

很明顯殴边,這是一個(gè)語(yǔ)法錯(cuò)誤(大括號(hào)后多了一個(gè)分號(hào))。
現(xiàn)在的編譯器會(huì)自動(dòng)檢測(cè)自動(dòng)忽略分號(hào)唆涝,不會(huì)報(bào)錯(cuò)找都,但是我們還是希望能跑在老的編譯器上唇辨。

在沒(méi)有do/while(0)的情況下廊酣,在所有可能情況下,期望我們寫的多語(yǔ)句宏總能有正確的表現(xiàn)幾乎是不可能的赏枚。
如果我們使用do{...}while(0)來(lái)定義宏亡驰,即:

#define DOSOMETHING() \
        do{ \
          foo1();\
          foo2();\
        }while(0)\

這樣,宏被展開后饿幅,上面的調(diào)用語(yǔ)句才會(huì)保留初始的語(yǔ)義凡辱。do能確保大括號(hào)里的邏輯能被執(zhí)行,而while(0)能確保該邏輯只被執(zhí)行一次栗恩,就像沒(méi)有循環(huán)語(yǔ)句一樣透乾。

總結(jié):在Linux和其它代碼庫(kù)里的,很多宏實(shí)現(xiàn)都使用do/while(0)來(lái)包裹他們的邏輯磕秤,這樣不管在調(diào)用代碼中怎么使用分號(hào)和大括號(hào)乳乌,而該宏總能確保其行為是一致的。
cocos2d-x中大量使用了這種宏定義:
#define CC_SAFE_DELETE(p) do { if(p) { delete (p); (p) = 0; } } while(0)

2. 避免使用goto控制程序流

在一些函數(shù)中市咆,我們可能需要在return語(yǔ)句之前做一些清理工作汉操,比如釋放在函數(shù)開始處由malloc申請(qǐng)的內(nèi)存空間,使用goto總是一種簡(jiǎn)單的方法:

int foo()
{
    somestruct *ptr = malloc(...);

    dosomething...;
    if(error)
        goto END;
    dosomething...;
    if(error)
        goto END;
    dosomething...;
END:
    free(ptr);
    return 0;
}

但由于goto不符合軟件工程的結(jié)構(gòu)化蒙兰,而且有可能使得代碼難懂磷瘤,所以很多人都不倡導(dǎo)使用芒篷,這個(gè)時(shí)候我們可以使用do{...}while(0)來(lái)做同樣的事情:

int foo()
{
    somestruct *ptr = malloc(...);
    do
    {
        dosomething...;
        if(error)
            break;
        dosomething...;
        if(error)
            break;
        dosomething...;
    }
    while(0);

    free(ptr);
    return 0;
}

這里將函數(shù)主體部分使用do{...}while(0)包含起來(lái),使用break來(lái)代替goto采缚,后續(xù)的清理工作在while之后针炉,現(xiàn)在既能達(dá)到同樣的效果,而且代碼的可讀性扳抽、可維護(hù)性都要比上面的goto代碼好的多了糊识。

我經(jīng)常使用這個(gè)種技能在Lua里,Lua不支持do{...}while(0)語(yǔ)法摔蓝,但是Lua有一種類似的語(yǔ)法repeat...until赂苗,偽代碼如下:

repeat
  dosomething...
  if error then
    break;
  end
  dosomething...;
  if error then
    break;
  end      
  dosomething...;
until (1);
print("break repeat");

這樣和do{...}while(0)一樣,也保證了只執(zhí)行一次贮尉,可以用break調(diào)出循環(huán)拌滋。

3. 避免由宏引起的警告

內(nèi)核中由于不同架構(gòu)的限制,很多時(shí)候會(huì)用到空宏猜谚,败砂。在編譯的時(shí)候,這些空宏會(huì)給出warning魏铅,為了避免這樣的warning昌犹,我們可以使用do{...}while(0)來(lái)定義空宏:
#define EMPTYMICRO do{}while(0)
這種情況不太常見,因?yàn)橛泻芏嗑幾g器览芳,已經(jīng)支持空宏斜姥。

4. 定義單一的函數(shù)塊來(lái)完成復(fù)雜的操作

如果你有一個(gè)復(fù)雜的函數(shù),變量很多沧竟,而且你不想要增加新的函數(shù)铸敏,可以使用do{...}while(0),將你的代碼寫在里面悟泵,里面可以定義變量而不用考慮變量名會(huì)同函數(shù)之前或者之后的重復(fù)杈笔。
但是我不建議這樣做,盡量聲明不同的變量名糕非,以便于后續(xù)開發(fā)人員閱讀蒙具。

int key;
string value;
int func()
{
    int key = GetKey();
    string value = GetValue();
    dosomething for key,value;
    do{
        int key;string value;
        dosomething for this key,value;
    }while(0);    
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市朽肥,隨后出現(xiàn)的幾起案子禁筏,更是在濱河造成了極大的恐慌,老刑警劉巖鞠呈,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件融师,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蚁吝,警方通過(guò)查閱死者的電腦和手機(jī)旱爆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門舀射,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人怀伦,你說(shuō)我怎么就攤上這事脆烟。” “怎么了房待?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵邢羔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我桑孩,道長(zhǎng)拜鹤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任流椒,我火速辦了婚禮敏簿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宣虾。我一直安慰自己惯裕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布绣硝。 她就那樣靜靜地躺著蜻势,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹉胖。 梳的紋絲不亂的頭發(fā)上握玛,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音次员,去河邊找鬼败许。 笑死,一個(gè)胖子當(dāng)著我的面吹牛淑蔚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愕撰,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼刹衫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了搞挣?” 一聲冷哼從身側(cè)響起带迟,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎囱桨,沒(méi)想到半個(gè)月后仓犬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舍肠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年搀继,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窘面。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叽躯,死狀恐怖财边,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情点骑,我是刑警寧澤酣难,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站黑滴,受9級(jí)特大地震影響憨募,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袁辈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一馋嗜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吵瞻,春花似錦葛菇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至卿泽,卻和暖如春莺债,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背签夭。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工齐邦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人第租。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓措拇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親慎宾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丐吓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 實(shí)際上,do{...}while(0)的作用遠(yuǎn)大于美化你的代碼趟据。查了些資料券犁,總結(jié)起來(lái)這樣寫主要有以下幾點(diǎn)好處: 1...
    WB莫遙燚閱讀 784評(píng)論 0 0
  • 參考:http://www.spongeliu.com/415.htmllinux內(nèi)核和其他一些開源的代碼中,經(jīng)常...
    zfl1024閱讀 3,563評(píng)論 0 1
  • 1汹碱、輔助定義復(fù)雜的宏粘衬,避免引用的時(shí)候出錯(cuò): 舉例來(lái)說(shuō),假設(shè)你需要定義這樣一個(gè)宏: 這個(gè)宏的本意是,當(dāng)調(diào)用DOSOM...
    LearningCoding閱讀 2,683評(píng)論 0 3
  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line)稚新,也就是一...
    悟名先生閱讀 4,151評(píng)論 0 13
  • 01 我是一個(gè)普通民事案件勘伺,出生于一個(gè)偏僻的鄉(xiāng)鎮(zhèn)。并且生得猝不及防枷莉。 那天早上娇昙,張三在外面轉(zhuǎn)悠回來(lái),對(duì)門李四家的藏...
    清能有容閱讀 3,161評(píng)論 1 3