在看ldlm的代碼過程中遇到了一個很奇怪的問題,有很多宏定義使用了do while(0)這種看起來好像沒啥用的代碼。然后我就問問師兄,才得知,這種用法很常見,自己又查了一下資料,原來在linux內核代碼中經常用到這個東西!現在就將這個東西整理一下。
為什么在內核中碰到很多 #define ... do{...} while(0) ?
有以下幾點原因:
1、空語句在編譯的時候會出現警告,所以有必要用#define FOO do{ } while(0)
2、給定一個基本塊,可以在里面定義局部變量
3、為了能夠在條件語句中使用復雜的宏定義。例如下面這段代碼:
#define FOO(x) \
printf("arg is %s\n", x); \
do_something_useful(x);
如果這樣用:
if (blah == 2)
F00(blah);
將會被展開為:
if (blah == 2)
printf("arg is %s\n", blah);
do_something_useful(blah);
這樣,if條件之后包含了printf()語句,而do_something_useful()調用不能按照預期那樣工作。而是用do {...} while(0)定義后,就會展開成以下語句:
if (blah == 2)
do{
printf("arg is %s\n", blah);
do_something_useful(blah);
}while(0);
這是我們所期望的。
如果你希望定義一個包含多行語句和一些局部變量的時候. 一般的定義方式只能這樣:
#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }
然而在某些情況下,這樣並不能正常工作. 下面是包含兩個分支的if語句:
if (x > y)
exch(x,y); // Branch 1
else
do_something(); // Branch 2
但這樣卻只能展開成單分支的if語句,如下:
if (x > y) { // 單分支if
int tmp;
tmp = x;
x = y;
y = tmp;
}
; // 空語句
else // 錯誤!!! "parse error before else"
do_something();
問題是由於在語句塊后直接加入分號(;)引起的. 解決辦法是將語句塊放入 do 和 while (0)中間.這樣就得到了一條單語句, 而不是被編譯器判斷為語句塊.現在的if語句如下:
if (x > y)
do {
int tmp;
tmp = x;
x = y;
y = tmp;
} while(0);
else
do_something();
假設有這樣一個宏定義
#define macro(condition) if(condition) dosomething();
現在在程序中這樣使用這個宏:
if(temp)
macro(i);
else
doanotherthing();
一切看起來很正常,但是仔細想想。這個宏會展開成:
if(temp)
if(condition) dosomething();
else
doanotherthing();
這時的else不是與第一個if語句匹配,而是錯誤的與第二個if語句進行了匹配,編譯通過了,但是運行的結果一定是錯誤的。
為了避免這個錯誤,我們使用do{….}while(0) 把它包裹起來,成為一個獨立的語法單元,從而不會與上下文發生混淆。同時因為絕大多數的編譯器都能夠識別do{…}while(0)這種無用的循環並進行優化,所以使用這種方法也不會導致程序的性能降低。
參考資料
http://blog.csdn.net/chenhu_doc/article/details/856468
http://www.233.com/linux/fudao/20101102/140715235.html
