在嵌入式開發的過程中,我們經常可以在一些優秀開源代碼的頭文件裏發現一些宏定義使用了do {} while(0)語句,也許你會疑惑do {} while(0)中的代碼不就是隻執行一次嗎,爲什麼還要多此一舉使用do {} while(0)循環結構去包裹呢?實際上,do {} while(0)的作用很大,下面可以看幾個例子。
一、定義複雜宏避免邏輯或編譯錯誤
假如你定義了一個宏,這個宏的作用爲連續調用2個函數,宏定義如下:
#define DOSOMETHING() func1(); func2()
但使用時可能會出現以下這樣的情況:
展開前:
if(judge == TRUE)
DOSOMETHING;
展開後:
if(judge == TRUE)
func1();
func2();
可以看到,由於沒有加上括號,func2是必會執行的,已經引起了邏輯錯誤;如果這個if還有else等分支的話,會出現編譯錯誤。爲了避免這個錯誤,也許你會給宏加上括號,就像下面這樣:
#define DOSOMETHING() { func1(); func2();}
使用時再次展開宏:
展開前:
if(judge == TRUE)
DOSOMETHING;
展開後
if(judge == TRUE)
{
func1();
func2();
};
可以看到,由於調用DOSOMETHING宏時,後面有個“;”號,所以展開後最後多了一個“;”號,顯然如果這個符號是多餘的,雖然有些編譯器可以忽略多餘的“;”,但如果這個if後面有個else語句,也會引起編譯的異常。也許你會說,最後的“;”是多餘的,那我調用的時候不加“;”不就可以了嗎,就像下面這樣。
if(judge == TRUE)
DOSOMETHING
當然這是行得通的, 但是在每個語句後面加分號是一種約定俗成的習慣,應當極力避免這種使用方式,否則閱讀代碼將是一件痛苦的事情!這時候do {} while(0)就派上用場了!我們可以用do {} while(0)重新定義DOSOMETHING宏:
#define DOSOMETHING() do{ func1(); func2();} while(0)
再次展開宏,就可以看到,使用上就毫無問題了! do {} while(0)真香啊!
展開前:
if(judge == TRUE)
DOSOMETHING;
展開後:
if(judge == TRUE)
do{
func1();
func2();
}while(0);
二、代替goto語句完成函數退出邏輯
雖然儘量不使用goto是C語言開發中不成文的規定,但實際上我們經常使用goto語句來完成一些清理工作:
int func()
{
bool error = false;
void *ptr = malloc(1024);
error = dosomething1();
if(error)
goto exit;
error = dosomething2();
if(error)
goto exit;
exit:
free(ptr);
return 0;
}
我們可以使用do {} while(0)結構來代替這種goto的用法,避免goto語句的使用。
int func()
{
bool error = false;
void *ptr = malloc(1024);
do{
error = dosomething1();
if(error)
break;
error = dosomething2();
if(error)
break;
}while(0);
free(ptr);
return 0;
}