在看《C語言高級編程》時,裏面有個關於宏##的題目:html
1.已知#define A 「menu」
#define B 「osd」,
若請使用宏A,B表示出字符串」menuosd」
答案:1 答案1:#define C A B
答案2:#define _C_(a,b) a##b
#define C(a,b) _C_(a,b)
而後我實際動手測試了一下,先來第一種:編程
#include <stdio.h> #define A "menu" #define B "osd" #define STR A B int main(int argc, char *argv[]) { char *p = STR; return 0; }
gcc -E hell.c -o hello.i
cat hello.i
結果:測試
int main(int argc, char *argv[])
{
char *p = "menu" "osd";
return 0;
}
第一個答案其實預編譯後給出的結果是不徹底符合要求的。google
而後是第二種:spa
#include <stdio.h> #define A "menu" #define B "osd" #define _C_(a,b) a##b #define C(a,b) _C_(a,b) int main(int argc, char *argv[]) { char *p = C(A,B); printf("%s\n", p); return 0; }
首先,爲何要定義兩個宏,一個不能解決問題嗎?是的,不能。爲何?看這個連接:[短小精悍的宏](http://www.cnblogs.com/wb-DarkHorse/archive/2013/04/27/3046749.html)
而後再次按照上邊的命令進行預編譯,可是給出了錯誤信息:pasting "menu" and "osd" does not give a valid preprocessing token gcc
這就奇怪了。而後google了一下,發現了相同的問題:
[問題](http://stackoverflow.com/questions/4667779/preprocessor-macro-gcc-pasting-x-and-x-does-not-give-a-valid-preprocessing-toke)code
而且裏面說了,這種狀況在VS裏面不會報錯,能夠直接工做。so?htm
#include <stdio.h> #define A "menu" #define B "osd" #define _C_(a,b) a##b #define C(a,b) _C_(a,b) int _tmain(int argc, _TCHAR* argv[]) { char *p = C(A,B);//STR; printf("%s\n", p); return 0; }
果真給出告終果:menuosdblog
爲何gcc和VS會對這個問題給出差別的結果呢?看這個問題:
[that's why](http://stackoverflow.com/questions/1206624/differences-in-macro-concatenation-operator-between-visual-c-and-gcc?rq=1)token
根據C標準,用##操做後的結果必須是一個已經預約義過的符號。不然是未定義的。因此gcc和vs對於這個未定義行爲表示了不一樣的見解,前者是給出錯誤,後者一笑而過。那什麼是已經預約過的符號呢? 它包含了這些:頭文件名, 等式, 預處理數字, 字符常數, 字符串值, 標點符號, 單個非空字符字符串
在咱們的例子中,_C_(a,b)用##鏈接後,應該是產生menuosd,可是這是一個未預約義的字符串,因此產生了一個未定義的行爲。咱們再看一個例子:
#define A 2 #define _CONS(a,b) (a##e##b) #define CONS(a,b) _CONS(a,b) int main(int argc, char *argv[]) { printf("%f\n", CONS(A, A)); return 0; }
這個時候gcc不會給出錯誤提示了。結果:200.0000
爲何這個時候不給出錯誤提示呢?個人理解是,CONS(A, A)替換後成爲2e2,而這時一個常量,符合C標準。
ok,給出一個連接,詳細的解釋了gcc中##的用法:
[gcc concatenation](http://gcc.gnu.org/onlinedocs/gcc-4.3.3/cpp/Concatenation.html#Concatenation)