do{}while(0)。do{conditional code}while(condition)結構流程圖如下:
一般結構如以下代碼:
do
{
??//循環體
}
while?(條件表達式);
意思是先干了再說!!
意思是先看看能不能干!
linux內核和其他一些開源的代碼中,經常會遇到這樣的代碼:
do{
?...
}while(0)
這樣的代碼一看就不是一個循環,do..while表面上在這里一點意義都沒有,只執行一次而已,那么為什么要這么用呢?
實際上,do{...}while(0)的作用可不止一點點,下面我列舉了一些。
cocos2d-x代碼中。do
{
????CCImage*?pImage?=?new?CCImage();
????CC_BREAK_IF(NULL?==?pImage);
????bRet?=?pImage->initWithString(text,?(int)dimensions.width,?(int)dimensions.height,?eAlign,?fontName,?(int)fontSize);
????CC_BREAK_IF(!bRet);
????bRet?=?initWithImage(pImage);
????CC_SAFE_RELEASE(pImage);
}?while?(0);
舉例來說,假設你需要定義這樣一個宏:
#define?DOSOMETHING()?action1();?action2();
這個宏的本意是,當執行DOSOMETHING()時,action1(),action2()都會被調用。如果有判斷,再執行這個宏的話,如下:
if(NULL?==?pPointer)
??DOSOMETHING();
else
??...
這樣宏在預處理的時候會直接被展開,放在花括號里,那么實際上寫的代碼如下:
if(NULL?==?pPointer)
????action1();
action2();
else
??...
這展開存在兩個問題:
那么,僅僅使用{}把action1()、action2()包起來行么?比如:
#define?DOSOMETHING()?{?action1();?action2();?}
我們在寫代碼的時候都習慣在語句右面加上分號,如果在宏中使用{},代碼編譯展開后宏就相當于這樣寫了:{...};,展開后如下:
if(NULL?==?pPointer)
{
????action1();
????action2();
};
else
??...
這段代碼中大括號后多了一個分號,如果有else,那么else又沒有對應的if了,編譯出錯。
如果我們使用do{...}while(0)來定義宏,即:
#define?DOSOMETHING()?\
????????do{?\
??????????action1();\
??????????action2();\
????????}while(0)\
宏被展開后,上面的調用語句會保留初始的語義,同時絕大部分編譯器都能夠識別do{...}while(0)這種無用的循環并進行優化,不會導致性能優化的降低。
在Linux內核和驅動代碼還有cocos2d-x中,很多宏實現都使用do{...}while(0)來包裹他們的邏輯,Google的Robert Love(先前從事Linux內核開發)給我們解答如下:
“讓你定義的宏總是以相同的方式工作,不管在調用代碼中怎么使用分號和大括號,而該宏總能確保其行為是一致的。
do{...}while(0)循環中,則能用break達到這個目的。do
{
??執行.
??再執行…
??if?(如果有什么條件滿足)
??{
????我想跳到另外一段代碼了,剩下的不執行了,可是不建議用goto語句,怎么辦呢?
?????break;/*搞定*/
??}
??我有可能被執行.
}while(false)
舉個例子如下:
do
{
??if(!a)?break;
??//do?something?here
??if(!b)?break;
??//do?another?thing?here???
}while(0);
#include?
#include?
int?main()
{
???char?*str;
?
???/*?最初的內存分配?*/
???str?=?(char?*)?malloc(15);
???if(str?!=?NULL)
?????goto?loop;
?
???printf("hello?world\n");
?
loop:
???printf("malloc?success\n");
???return(0);
}
但是,由于goto不符合軟件工程的結構化,而且有可能使得代碼難懂,所以很多人都不倡導使用,這個時候我們可以使用do{...}while(0)來做同樣的事情:
#include?
#include?
int?main()
{
??do{
??????char?*str;
??????/*?最初的內存分配?*/
??????str?=?(char?*)?malloc(15);
??????if(str?!=?NULL)
???????break;
??????printf("hello?world\n");
??}while(0);
???printf("malloc?success\n");
???return(0);
}
這里將函數主體部分使用do{...}while(0)包含起來,使用break來代替goto,后續的清理工作在while之后,現在既能達到同樣的效果,而且代碼的可讀性、可維護性都要比上面的goto代碼好的多了。
int?a;
a?=?10;
int?b;
b?=?20;
這種代碼在只支持c89的編譯器上是編譯不過去的,比如ADS 2.0。
int?a;
a?=?10;
do
{
???int?b;
???b?=?20;
}while(0);
do{...}while(0)來定義空宏:#define?DOSOMETHING()?do{}while(0)
如果你有一個復雜的函數,變量很多,而且你不想要增加新的函數,可以使用do{...}while(0),將你的代碼寫在里面,里面可以定義變量而不用考慮變量名會同函數之前或者之后的重復,例如:
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);????
}
但為了代碼的可讀性,盡量聲明不同的變量名,以便于后續開發人員欣賞。

END