4 回答

TA貢獻(xiàn)1804條經(jīng)驗 獲得超8個贊
該do ... while
和if ... else
在那里讓這個后您的宏分號總是意味著同樣的事情。假設(shè)你有類似第二個宏的東西。
#define BAR(X) f(x); g(x)
現(xiàn)在,如果你要BAR(X);
在一個if ... else
語句中使用if語句的主體沒有用大括號括起來,你會得到一個不好的驚喜。
if (corge) BAR(corge);else gralt();
上面的代碼將擴(kuò)展為
if (corge) f(corge); g(corge);else gralt();
這在語法上是不正確的,因為else不再與if相關(guān)聯(lián)。在宏中用大括號括起來是沒有用的,因為大括號后面的分號在語法上是不正確的。
if (corge) {f(corge); g(corge);};else gralt();
有兩種方法可以解決問題。第一種方法是使用逗號對宏中的語句進(jìn)行排序,而不會使其具有像表達(dá)式一樣的能力。
#define BAR(X) f(X), g(X)
上面的bar版本BAR
將上面的代碼擴(kuò)展為以下代碼,這在語法上是正確的。
if (corge) f(corge), g(corge);else gralt();
如果不是f(X)
你有一個更復(fù)雜的代碼體,需要進(jìn)入它自己的塊,比如聲明局部變量,這就行不通了。在最一般的情況下,解決方案是使用類似的東西do ... while
使宏成為一個單獨的語句,分號不會混淆。
#define BAR(X) do { \ int i = f(X); \ if (i > 4) g(i); \} while (0)
你不必使用do ... while
,你也可以做一些東西if ... else
,雖然當(dāng)它if ... else
內(nèi)部擴(kuò)展時會if ... else
導(dǎo)致“ 懸掛其他 ”,這可能使現(xiàn)有懸掛的其他問題更難找到,如下面的代碼。
if (corge) if (1) { f(corge); g(corge); } else;else gralt();
關(guān)鍵是在懸掛分號錯誤的情況下用掉分號。當(dāng)然,在這一點上它可能(并且可能應(yīng)該)被認(rèn)為最好將其聲明BAR
為實際函數(shù),而不是宏。
總之,do ... while
可以解決C預(yù)處理器的缺點。當(dāng)那些C風(fēng)格指南告訴你裁掉C預(yù)處理器時,這是他們擔(dān)心的事情。

TA貢獻(xiàn)1811條經(jīng)驗 獲得超5個贊
宏是復(fù)制/粘貼的文本,預(yù)處理器將放入正版代碼中; 宏的作者希望替換產(chǎn)生有效的代碼。
有三個好的“提示”可以成功:
幫助宏的行為像真正的代碼
正常代碼通常以分號結(jié)束。如果用戶查看不需要的代碼......
doSomething(1) ;DO_SOMETHING_ELSE(2) // <== Hey? What's this?doSomethingElseAgain(3) ;
這意味著如果沒有分號,用戶希望編譯器產(chǎn)生錯誤。
但真正正確的理由是,在某些時候,宏的作者可能需要用真正的函數(shù)(也許是內(nèi)聯(lián)函數(shù))替換宏。所以宏應(yīng)該真的像一個。
所以我們應(yīng)該有一個需要分號的宏。
生成有效的代碼
如jfm3的答案所示,有時宏包含多條指令。如果宏在if語句中使用,這將是有問題的:
if(bIsOk) MY_MACRO(42) ;
此宏可以擴(kuò)展為:
#define MY_MACRO(x) f(x) ; g(x)if(bIsOk) f(42) ; g(42) ; // was MY_MACRO(42) ;
g
無論值如何,都將執(zhí)行該功能bIsOk
。
這意味著我們必須向宏添加一個范圍:
#define MY_MACRO(x) { f(x) ; g(x) ; }if(bIsOk) { f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
生成有效的代碼2
如果宏是這樣的:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
我們可能在以下代碼中遇到另一個問題:
void doSomething(){ int i = 25 ; MY_MACRO(32) ;}
因為它會擴(kuò)展為:
void doSomething(){ int i = 25 ; int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;}
當(dāng)然,這段代碼不會編譯。所以,再一次,解決方案是使用范圍:
#define MY_MACRO(x) { int i = x + 1 ; f(i) ; }void doSomething(){ int i = 25 ; { int i = 32 + 1 ; f(i) ; } ; // was MY_MACRO(32) ;}
代碼再次正常運行。
結(jié)合分號+范圍效應(yīng)?
有一個產(chǎn)生這種效果的C / C ++習(xí)語:do / while循環(huán):
do{ // code}while(false) ;
do / while可以創(chuàng)建一個范圍,從而封裝宏的代碼,最后需要一個分號,從而擴(kuò)展為需要一個代碼的代碼。
獎金?
C ++編譯器將優(yōu)化do / while循環(huán),因為其后置條件為false的事實在編譯時是已知的。這意味著像一個宏:
#define MY_MACRO(x) \do \{ \ const int i = x + 1 ; \ f(i) ; g(i) ; \} \while(false)void doSomething(bool bIsOk){ int i = 25 ; if(bIsOk) MY_MACRO(42) ; // Etc.}
將正確擴(kuò)展為
void doSomething(bool bIsOk){ int i = 25 ; if(bIsOk) do { const int i = 42 + 1 ; // was MY_MACRO(42) ; f(i) ; g(i) ; } while(false) ; // Etc.}
然后編譯和優(yōu)化
void doSomething(bool bIsOk){ int i = 25 ; if(bIsOk) { f(43) ; g(43) ; } // Etc.}

TA貢獻(xiàn)1772條經(jīng)驗 獲得超5個贊
您可能還想補(bǔ)充一點,宏語法也可以使用簡單的“if”語句防止可能更危險(因為沒有錯誤)的意外行為:
#define FOO(x) f(x); g(x)if (test) FOO( baz);
擴(kuò)展為:
if (test) f(baz); g(baz);
這在語法上是正確的,所以沒有編譯器錯誤,但可能有意外的結(jié)果,g()將始終被調(diào)用。

TA貢獻(xiàn)1757條經(jīng)驗 獲得超7個贊
上述答案解釋了這些結(jié)構(gòu)的含義,但兩者之間存在顯著差異,未提及。其實,還有一個原因,更喜歡do ... while
到if ... else
結(jié)構(gòu)。
if ... else
構(gòu)造的問題是它不會強(qiáng)迫你輸出分號。喜歡這段代碼:
FOO(1)printf("abc");
雖然我們遺漏了分號(錯誤地),但代碼將擴(kuò)展為
if (1) { f(X); g(X); } elseprintf("abc");
并將靜默編譯(雖然一些編譯器可能會發(fā)出無法訪問代碼的警告)。但該printf
聲明永遠(yuǎn)不會被執(zhí)行。
do ... while
構(gòu)造沒有這樣的問題,因為之后唯一有效的標(biāo)記while(0)
是分號。
- 4 回答
- 0 關(guān)注
- 870 瀏覽
添加回答
舉報