在閱讀linux內核代碼的過程中,經常會發現宏定義中使用了do-while語句。有時候覺得這種do-while語句顯得有點多餘?幹嘛非得使用它把函數塊包裹起來?像下面的
#define MARCO_FUN1() do{\
Function();
}while(0)
幹嘛不直接#define MARCO_FUN1 Function();就ok了。
後來想到如果MARCO_FUN1除了要實現Function功能外,還要實現Function2。如果仍舊直接定義的話是很容易出錯的,像這種:
for(;;)MARCO_FUN1();
那麼for循環只會將function1包括進去了!
當然這個是比較容易想到的破綻之一,更多的解釋如下:
1,空的宏定義避免warning:
#definefoo() do{}while(0)
2,存在一個獨立的block,可以用來進行變量定義,進行比較複雜的實現。
3,如果出現在判斷語句過後的宏,這樣可以保證作爲一個整體來是實現:
#definefoo(x) \
action1();\
action2();
在以下情況下:
if(NULL== pPointer)
foo();
就會出現action1和action2不會同時被執行的情況,而這顯然不是程序設計的目的。
4,以上的第3種情況用單獨的{}也可以實現,但是爲什麼一定要一個do{}while(0)呢,看以下代碼:
#defineswitch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
if(x>y)
switch(x,y);
else //error, parse error before else
otheraction();
在把宏引入代碼中,會多出一個分號,從而會報錯。
//------------------------------------------------
使用do{….}while(0) 把它包裹起來,成爲一個獨立的語法單元,
從而不會與上下文發生混淆。同時因爲絕大多數的編譯器都能夠識別do{…}while(0)這種無
用的循環並進行優化,所以使用這種方法也不會導致程序的性能降低。
也許你會說,我們代碼的習慣是在每個判斷後面加上{}, 就不會有這種問題了,也就不需要do...while了,如:
if(...)
{
}
else
{
}
誠然,這是一個好的,應該提倡的編程習慣,但一般這樣的宏都是作爲library的一部分出現的,而對於一個library的作者,他所要做的就是讓其庫具有通用性,強壯性,因此他不能有任何對庫的使用者的假設,如其編碼規範,技術水平等。