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);
}