Clang 宏定義初探(二)
本篇總結(jié)下這幾天看的宏的一些看到的用法。
1、參數(shù)粘結(jié)
這是一個(gè)類似 shell 之類的腳本語(yǔ)言的特性,可以利用這個(gè)特性完成一些重復(fù)度比較高的編碼的簡(jiǎn)化。
例如,對(duì)proc文件系統(tǒng)進(jìn)行綁定的時(shí)候,需要在/proc/test/目錄下,簡(jiǎn)歷3個(gè)文件接口,test1、test2、test3.
可以這樣寫(xiě)
#define BIND(x) test##x->read_proc=test##x##_read
在使用的時(shí)候,就可以
BIND(1); //展開(kāi)為test1->read_proc=test1_read;
BIND(2); //展開(kāi)為test2->read_proc=test2_read;
不管是從語(yǔ)義還是編碼復(fù)雜度,都降低了。
2、參數(shù)字符化
在使用單個(gè) # 號(hào),作為函數(shù)式宏的參數(shù)前綴時(shí),可以讓宏的內(nèi)容變成字符串,比如說(shuō):
#define print(x) do{\
printf(#x);\
printf("=%d\n",x);\
}while(0)
使用的時(shí)候,直接寫(xiě):
int t = 1;
print(t);
結(jié)果會(huì)是
t=1,這個(gè)在做日志的時(shí)候還是非常好用的。
3、do{...}while(0) 和 ({...})
可以認(rèn)為是前者是 void 函數(shù),后者是有 return 值的函數(shù)。
入2中所示,do{...}while(0) 是為了產(chǎn)生一個(gè)程序塊,當(dāng)宏里有多條需要語(yǔ)句需要執(zhí)行時(shí),如果不適用這種do{...}while(0)的形式,可能導(dǎo)致一些隱形的錯(cuò)誤,例如:
#define print(x) {printf(#x);printf("=%d\n",x);
正常的:
print(t); 是沒(méi)有問(wèn)題的,但是如果放在程序段里:
if( flag )
print(t);
else
print(a);
展開(kāi)之后,會(huì)發(fā)現(xiàn)為
if( flag )
{printf("t");printf("=%d\n",t);};
else
這個(gè)語(yǔ)法就錯(cuò)了。因此,當(dāng)代碼段比較多,且不需要返回值時(shí)就用 do{...}while(0)吧。
另外一種方式屬于 GNU 的擴(kuò)展,后續(xù)在看。
4、多重展開(kāi)
還是基于打印的例子,我需要打印一些列舉的參數(shù)值:
#define P(x) arg##x
#define print(x) do{printf(#P(x));printf("=%d\n",P(x));}while(0)
這個(gè)編譯通不過(guò),換成以下方式即可:
#define P(x) arg##x
#define __print(x) do{printf(#x);printf("=%d\n",x);}while(0)
#define _print(x) __print(x)
#define print(x) _print(P(x))
修改成這樣,解決了想要的解決的問(wèn)題:
輸出結(jié)果為:arg1=1
主要涉及的問(wèn)題在于宏的多次展開(kāi),宏每次展開(kāi)只會(huì)對(duì)當(dāng)前的輸入?yún)?shù)進(jìn)行一次展開(kāi),當(dāng)你的輸入值也是個(gè)宏的時(shí)候,就需要使用過(guò)度宏,讓你的輸入接著展開(kāi)。
對(duì)于多次展開(kāi)沒(méi)有從最根本的原理解釋,只是從實(shí)驗(yàn)感官上對(duì)這個(gè)特性做了分析,實(shí)際上,自己也不會(huì)寫(xiě)出那么復(fù)雜的宏(怕中間調(diào)用出漏洞)。
宏的基本常見(jiàn)用法,都差不多枚舉了一番,往后在見(jiàn)到更高級(jí)的玩法和比較精髓的寫(xiě)法往后再慢慢補(bǔ)充上來(lái)吧,另外GNU的擴(kuò)展也會(huì)在后篇繼續(xù)學(xué)習(xí)了解。