零.導(dǎo)引 第一次見到 do{...}while(0)是在學(xué)習(xí)libevent的時(shí)候,看到里面有很多類似
#define TT_URI(want) do { \ char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \ tt_want(ret == url_tmp); \ if (strcmp(ret,want) != 0) \ TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \
當(dāng)時(shí)特別疑惑,do{...}while()不是做循環(huán)的嗎,類似for,while的語法,不過現(xiàn)實(shí)開發(fā)中,用for和while的比較多,do{...}while()比較少了,算是比較不常用的語法。 但是在這里,這樣的代碼一看就不是一個(gè)循環(huán),do..while表面上在這里一點(diǎn)意義都沒有,那么為什么要這么用呢?特別疑惑的google之,恍然大悟,原來do{...}while()還有此等妙用,看來自己還差得遠(yuǎn)啊。
總體來說,do{...}while(0)有兩種用法。
一.定義宏,實(shí)現(xiàn)局部作用域。
1.大家做c語言題目的時(shí)候,一道必考題就是 #define的算術(shù)運(yùn)算。 比如,我隨手寫一個(gè)最簡單的#define
int result = 2 * FUNC(3);
result輸出多少? 26?錯(cuò)! 這是c語言新手一定會犯的錯(cuò)誤,至少我上大學(xué)的時(shí)候第一次看到這,我就做錯(cuò)了。 要知道這道題答案是多少,首先就要知道#define的作用。 1).#define M (a+b) 它的作用是指定標(biāo)識符M來代替表達(dá)式(a+b)。在編寫源程序時(shí),所有的(a+b)都可由M代替,而對源程序作編譯時(shí),將先由預(yù)處理程序進(jìn)行宏代換,即用(a+b)表達(dá)式去置換所有的宏名M,然后再進(jìn)行編譯。 2).c語言允許宏帶有參數(shù)。在宏定義中的參數(shù)稱為形式參數(shù),在宏調(diào)用中的參數(shù)稱為實(shí)際參數(shù)。對帶參數(shù)的宏,在調(diào)用中,不僅要宏展開,而且要用實(shí)參去代換形參。(以上兩句來自百度百科)
也就是 #define是在預(yù)處理的時(shí)候進(jìn)行直接替換!(這句話是這一節(jié)的重點(diǎn)) 例如之上的展開就是. int result = 2 * x * 3 + 4 x用實(shí)參3代替就是: int result = 2 * 3 * 3 + 4 = 22而不是26.
有些人可能說,這些我都知道,這跟do{...}while(0)有什么關(guān)系。
其實(shí),我只是為了告訴你,#define使用的時(shí)候要特別小心,尤其是#define一個(gè)很復(fù)雜的邏輯的時(shí)候。
我們舉個(gè)簡單的#define的例子:
#define LOG print();send(); cout <<"hello world"<<endl;
這個(gè)代碼輸出什么?理論上,if(false)里面的代碼不會被執(zhí)行,也就是LOG不會被執(zhí)行,所以只應(yīng)該打印出"hello world".
但是事實(shí)上:
納悶?
注意我上面說的一句話:
也就是 #define是在預(yù)處理的時(shí)候進(jìn)行直接替換!(這句話是這一節(jié)的重點(diǎn))
也就是說,上面的if(false)...在這里是:
cout <<"hello world"<<endl;
懂了吧。
怎么解決了,有些人馬上想到,用{...}把#define 的值括住不就可以了。的確,在這里是可以的。
我們在寫代碼的時(shí)候都習(xí)慣在語句右面加上分號,如果在宏中使用{},我們通常會這么寫:
#define LOG {print();send();};
當(dāng)我們的if后面有一個(gè)else呢?
就變成了:
這樣就會因?yàn)閕f語句后面多加了個(gè);而編譯不通過。不要說你說,那我不加;那要是你開發(fā)一個(gè)大型項(xiàng)目的時(shí)候你自己也不知道你自己要不要加;了,你就會被自己給繞暈了,所以統(tǒng)一的規(guī)范很重要。
那么來我們的最終版本:do{...}while(0);
#define LOG do{print();send();}while (0); cout <<"hello world"<<endl;
就相當(dāng)于:
cout <<"hello world"<<endl;
用do{...}while(0);包裹住要操作的#define,無論你外面怎么操作,都不會影響#define的操作。妙哉妙哉啊。
三.替代goto.
int error = dosomething();
當(dāng)然這只是一個(gè)簡單的例子,有些人說,我可以不用goto,在每一個(gè)goto調(diào)用的地方直接,那么加一個(gè)判斷,你就要加一條clear(),萬一你漏了呢?而且正常情況下,foo里面的if有很多個(gè),你要寫很多goto,END里面的邏輯也更復(fù)雜。這樣就更要小心。
由于goto不符合軟件工程的結(jié)構(gòu)化,而且有可能使得代碼難懂,所以很多人都不倡導(dǎo)使用,那這個(gè)時(shí)候就可以用do{}while(0)來進(jìn)行統(tǒng)一的管理:
int error = dosomething();
是不是看起來好看多了,而且還避免了由于錯(cuò)誤導(dǎo)致的嚴(yán)重bug(比如你在clear里面是清理內(nèi)存的操作,你忘記了寫goto,而走不到END里面)。
在do{...}while(0)里面,在任何地方都可以break跳出,然后繼續(xù)下面的執(zhí)行邏輯。即使你不寫break,也會在執(zhí)行完一遍do之后,while(0)不滿足,自己跳出去。
|