do while 0 的巧妙用處

2021-09-29 03:00:32 字數 2030 閱讀 9844

最近讀pjsip原始碼的時候看到dowhile(0)的這種用法:

於是查了下這種用法的作用,發現挺有意思的。

假設要定義乙個巨集:

#define f() f1(); f2();
這個巨集的意思是,當呼叫f()時,f1()和f2()都會被呼叫。但是在呼叫的時候如果這麼寫:

if(expr)

f();

而巨集在預處理的時候會直接被展開為:

if(expr)

f1();f2();

這就導致無論expr是否為真,f2()一定被執行,因為if語句只能控制下一句f1(),這並不是我們的預期。

一種解決辦法是用{}將f1();f2{};包起來:即

#define f()
但這種情況下,我們發現語句被展開為:

if(expr)

;

最後的分號是我們習慣新增的,顯然,這是個語法錯誤。有些編譯器並不能通過。

這時候我們發現dowhile(0)似乎是一種很好的解決辦法:

#define f() \

dowhile(0)\

可以達到語句被執行且僅執行一次,還能像普通函式呼叫一樣在後面加分號。

pjsip c原始碼中很多的這種用法:

#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這種簡單的**流控制效果,例如:有些函式中,在函式return之前我們經常會進行一些收尾的工作,比如free掉一塊函式開始malloc的記憶體,goto一直都是乙個比較簡便的方法。

int f()

...;

if(error)

...;

end:

free(ptr);

return 0;

}

但是goto不符合軟體工程的結構化,而且有可能使得**難懂,所以不倡導使用,這時可以用do{}while(0)來進行統一的管理:

int f()

...;

if(error)

...;

}while(0);

free(ptr);

return 0;

}

這裡將函式主體部分使用dowhile(0)包含起來,使用break來代替goto,後續的清理工作在while之後,現在既能達到同樣的效果,而且**的可讀性、可維護性都要比上面的goto**好的多了。

核心中由於不同架構的限制,很多時候會用到空巨集。在編譯的時候,這些空巨集會給出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...