有一道經典的C語言問題,關於宏定義中#和##符號的使用和宏定義展開問題
程序如下:
#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
int main()
{
printf("%s\n", h(f(1,2)));
printf("%s\n", g(f(1,2)));
return 0;
}
答案:第一行:12 第二行:f(1,2)
說明:
1、關於符號#和##
兩個符號都只能用於預處理宏擴展。不能在普通的源碼中使用它們,只能在宏定義中使用。
簡單的說,#是把宏參數變為一個字符串,##是把兩個宏參數連接在一起。
關於這兩個符號的具體意義和用法可以參見兩篇文章:
#和##在宏替換中的作用 http://www.linuxidc.com/Linux/2014-06/102921.htm
C/C++ 宏中"#"和"##"的用法 http://www.linuxidc.com/Linux/2014-06/102924.htm
還有GCC幫助文檔上的解釋:
3.4 Stringification
3.5 Concatenation
2、關於宏展開
預處理過程的幾個步驟:
1)字符集轉換(如三聯字符)
2)斷行鏈接/
3)注釋處理,/* comment */,被替換成空格
4)執行預處理命令,如#inlcude、#define、#pragma、#error等
5)轉義字符替換
6)相鄰字符串拼接
7)將預處理記號替換為詞法記號
第4)步即如何展開宏函數的規則:在展開當前宏函數時,如果形參有#或##則不進行宏參數的展開,否則先展開宏參數,再展開當前宏。
宏替換順序英文描述如下:
A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token, is replaced by the corresponding argument after all macros contained therein have been expanded.
3、總結
綜合以上,對於這道題來說,第一行h(f(1,2)),由於h(a)非#或者##所以先展開其參數f(1,2),即12,所以變成h(12),然後再宏替換為g(12),再次替換為12。
第二行g(f(1,2)),宏g(a)帶有#,所以裡面的f(1,2)不展開,所以變成f(1,2)
類似的這種問題在《你必須知道的495個C語言問題》中出現過,在121頁的“預處理功能”的問題11.19,有興趣的朋友可以看一看。
C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm
讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm
讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm
C++11 獲取系統時間庫函數 time since epoch http://www.linuxidc.com/Linux/2014-03/97446.htm
C++11中正則表達式測試 http://www.linuxidc.com/Linux/2012-08/69086.htm