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