最近讀pjsip原始碼的時候看到dowhile(0)的這種用法:
於是查了下這種用法的作用,發現挺有意思的。
假設要定義乙個巨集:
#define f() f1(); f2();
這個巨集的意思是,當呼叫f()時,f1()和f2()都會被呼叫。但是在呼叫的時候如果這麼寫:
而巨集在預處理的時候會直接被展開為:if(expr)
f();
這就導致無論expr是否為真,f2()一定被執行,因為if語句只能控制下一句f1(),這並不是我們的預期。if(expr)
f1();f2();
一種解決辦法是用{}將f1();f2{};包起來:即
#define f()
但這種情況下,我們發現語句被展開為:
最後的分號是我們習慣新增的,顯然,這是個語法錯誤。有些編譯器並不能通過。if(expr)
;
這時候我們發現dowhile(0)似乎是一種很好的解決辦法:
可以達到語句被執行且僅執行一次,還能像普通函式呼叫一樣在後面加分號。#define f() \
dowhile(0)\
pjsip c原始碼中很多的這種用法:
一些**中想達到goto這種簡單的**流控制效果,例如:有些函式中,在函式return之前我們經常會進行一些收尾的工作,比如free掉一塊函式開始malloc的記憶體,goto一直都是乙個比較簡便的方法。#if defined(pj_enable_extra_check) && pj_enable_extra_check != 0
# define pj_assert_on_fail(expr,exec_on_fail) \
do while (0)
#else
# define pj_assert_on_fail(expr,exec_on_fail) pj_assert(expr)
#endif
但是goto不符合軟體工程的結構化,而且有可能使得**難懂,所以不倡導使用,這時可以用do{}while(0)來進行統一的管理:int f()
...;
if(error)
...;
end:
free(ptr);
return 0;
}
這裡將函式主體部分使用dowhile(0)包含起來,使用break來代替goto,後續的清理工作在while之後,現在既能達到同樣的效果,而且**的可讀性、可維護性都要比上面的goto**好的多了。int f()
...;
if(error)
...;
}while(0);
free(ptr);
return 0;
}
核心中由於不同架構的限制,很多時候會用到空巨集。在編譯的時候,這些空巨集會給出warning,為了避免這樣的warning,可以使用dowhile(0)來定義空巨集:
#define emptymicro do{}while(0)
這種情況不太常見,因為有很多編譯器,已經支援空巨集。
複雜的函式,變數很多,若不想要增加新的函式,可以用dowhile(0),將**寫在裡面,裡面可以定義變數而不用考慮變數名會同函式之前或者之後的重複。
但是不建議這樣做,盡量宣告不同的變數名,以便於後續開發人員閱讀。
int i;
unsigned j;
int func()
while(0);
}
do while 0 在巨集定義中的巧妙用法
大家都知道,do while condition 可以表示迴圈,但你有沒有遇到在一些巨集定義中可以不用迴圈的地方,也用到了 do while.比如 define delete pointer p do while 0 這時,do while 0 的功能就不僅僅是迴圈了,這是do.while 0 的一...
巨集函式以及do while 0 的巧妙用法
巨集定義,編譯預處理命令,在編譯預處理時即進行簡單的字串替換。巨集定義函式和使用者定義的函式在使用時有如下區別 1 巨集函式會在編譯預處理時展開,只占用編譯時間,函式呼叫則會占用執行時間 分配單元 儲存現場 值傳遞 返回等 每次執行都需要載入,所以執行相對於巨集較慢。2 在函式呼叫時,先求出實參表示...
GNU C中的do while 0 用處
在c 中,有三種型別的迴圈語句 for,while,和do.while,但是在一般應用中作迴圈時,我們可能用for和while要多一些,do.while相對不受重視。但是,最近在讀我們專案的 時,卻發現了do.while的一些十分聰明的用法,不是用來做迴圈,而是用作其他來提高 的健壯性。1.do.w...