問題:下面通過宏定義實作一個可以指定字首的字元串。
PREFIX+".%d"
方法1:使用#運算符。出現在宏定義中的#運算符把跟在其後的參數轉換成一個字元串。有時把這種用法的#稱為字元串化運算符。
#include
#define PREX 1.3.6
#define FORMAT(n) #n".%d\n"
intmain()
{
intival = 246;
printf(FORMAT(PREX), ival);// PREX.246
return0;
}
但是輸出結果是:PREX.246,和預期的結果不一樣,宏PREX作為宏FORMAT的參數并沒有替換。那麼如何讓FORMAT宏的參數可以替換呢?
首先,C語言的宏是允許嵌套的,其嵌套後,一般的展開規律像函數的參數一樣:先展開參數,再分析函數,即由内向外展開。但是,注意:
(1) 當宏中有#運算符時,參數不再被展開;
(2) 當宏中有##運算符時,則先展開函數,再展開裡面的參數;
PS:
##運算符用于把參數連接配接到一起。預處理程式把出現在##兩側的參數合并成一個符号(非字元串)。
方法2:修改宏定義的格式,再添加一個中間宏TMP(x)實作對參數的替換,然後再替換為最終想要的字元串。
#define PREX 1.3.6
#define FORMAT(x) TMP(x)
#define TMP(x) #x".%d\n"
intmain()
{
intival = 246;
printf(FORMAT(PREX), ival);// 1.3.6.246
return0;
}
嵌套宏在某些情況下還是有一定的用處,但是我可能更願意定義一個函數宏來完成上面這個工作:
#include
#define FORMAT_FUN(szPrex, szFormat) do { \
charszTmp[128] = {0}; \
_snprintf(szTmp, sizeof(szTmp)-1,"%s", szFormat); \
_snprintf(szFormat, sizeof(szFormat)-1,"%s%s", szPrex, szTmp); \
} while(0); \
constchar*szPrex ="1.3.6";
intmain()
{
intival = 246;
charszFormat[128] =".%d\n";
FORMAT_FUN(szPrex, szFormat);
//printf("%s\n", szFormat);
printf(szFormat, ival);// 1.3.6.246
return0;
}
舉幾個關于宏嵌套用法的例子:
Ex. 1
#include
#define TO_STRING2(x) #x
#define TO_STRING(x) TO_STRING1(x)
#define TO_STRING1(x) #x
#define PARAM(x) #x
#define ADDPARAM(x) INT_##x
intmain()
{
constchar*str = TO_STRING(PARAM(ADDPARAM(1)));
printf("%s\n",str);
str = TO_STRING2(PARAM(ADDPARAM(1)));
printf("%s\n",str);
return0;
}
Ex. 2
#include
#define TO_STRING2(x) a_##x
#define TO_STRING(x) TO_STRING1(x)
#define TO_STRING1(x) #x
#define PARAM(x) #x
#define ADDPARAM(x) INT_##x
intmain()
{
constchar*str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));
printf("%s\n",str);
return0;
}
注意:例子2的代碼分别在不同的編譯器上輸出結果不相同。這是為什麼呢?
C99_TC3
6.10.3.1 Argument substitution
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. Aparameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument's preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
除非替換序列中的形式參數的前面有一個#符号,或者其前面或後面有一個##符号,否則,在插入前要對宏調用的實際參數記号進行檢查,并在必要時進行擴充。
改為:
#include
#define TO_STRING2(x) a_##x
#define TO_STRING(x) TO_STRING1(x)
#define TO_STRING1(x) T(x)
#define T(x) #x
#define PARAM(x) #x
#define ADDPARAM(x) INT_##x
intmain()
{
constchar*str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));
printf("%s\n",str);
return0;
}
Ex. 3
#include
intival = 0;
#define A(x) printf("%d\n", ival+=1);
#define B(x) printf("%d\n", ival+=2);
#define C() printf("%d\n", ival+=3);
intmain()
{
A(B(C()));
printf("%d\n", ival);// ?, 1
return0;
}
補充知識:
The C Programming Language, Second Edition
P.205 預處理
(1) 預處理器執行宏替換、條件編譯以及包含指定的檔案。
(2) 以#開頭的指令行("#"前可以有空格),就是預處理器處理的對象。
(3) 這些指令行的文法獨立于語言的其他部分,它們可以出現在任何地方,其作用可延續到所在翻譯單元的末尾(與作用域無關)。
(4) 行邊界是有實際意義的。每一行都将單獨進行分析。