C語言嵌套宏及# ##用法

關於嵌套宏的使用

--------------短小結論----------------------
涉及到宏定義展開順序的知識,如果宏替換以# ##爲前綴 ,則由外向內展開

 #define f(x) #x //結果將被擴展爲由實際參數替換該參數的帶引號的字符串
 #define b(x) a##x //連接實際參數
 #define ac hello
 int main(void)
 {
     f(b(c))//display "b(c)"
 }

如果最外層p(x)宏替換不以# ##爲前綴,則由內向外展開

#define f(x) #x
#define p(x) f(x)
#define b(x) a##x
#define ac hello
int main(void)
{
    p(b(c))// display "hello"
    return 0;
}

---------------分割線-----------------------------------

問題:下面通過宏定義實現一個可以指定前綴的字符串。
PREFIX+".%d"

方法1:使用#運算符。出現在宏定義中的#運算符把跟在其後的參數轉換成一個字符串。有時把這種用法的#稱爲字符串化運算符。

#include<cstdio>
#define PREX 1.3.6
#define FORMAT(n) #n".%d\n"

int main()
{
	int ival = 246;
	printf(FORMAT(PREX), ival);// PREX.246	
	return 0;
}

但是輸出結果是: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"

int main()
{
	int ival = 246;
	printf(FORMAT(PREX), ival);// 1.3.6.246	
	return 0;
}

嵌套宏在某些情況下還是有一定的用處,但是我可能更願意定義一個函數宏來完成上面這個工作:

#include <cstdio>
#define FORMAT_FUN(szPrex, szFormat) do { \
	char szTmp[128] = {0}; \
	_snprintf(szTmp, sizeof(szTmp)-1, "%s", szFormat); \
	_snprintf(szFormat, sizeof(szFormat)-1, "%s%s", szPrex, szTmp); \
} while(0); \
const char *szPrex = "1.3.6";

int main()
{
	int ival = 246;
	char szFormat[128] = ".%d\n";
	FORMAT_FUN(szPrex, szFormat);	
	//printf("%s\n", szFormat);
	printf(szFormat, ival);// 1.3.6.246	 
	return 0;
}

舉幾個關於宏嵌套用法的例子:

Ex. 1

#include <cstdio>
#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

int main()
{
	const char *str = TO_STRING(PARAM(ADDPARAM(1)));
	printf("%s\n",str);
	str = TO_STRING2(PARAM(ADDPARAM(1)));
	printf("%s\n",str);
	return 0;
}
/*
output:
"ADDPARAM(1)"
PARAM(ADDPARAM(1))
*/

Ex. 2
#include <stdio.h>
#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

int main()
{
	const char *str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));
	printf("%s\n",str);

	return 0;
}
/*
VS2010 output:
a_PARAM(ADDPARAM(1))
GCC 4.3.2 output:
a_PARAM(INT_1)
*/

注意:例子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 <stdio.h>
#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

int main()
{
	const char *str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));
	printf("%s\n",str);

	return 0;
}
/*
VS2010 output:
a_PARAM(INT_1)
GCC 4.3.2 output:
a_PARAM(INT_1)
(1) 對TO_STRING的參數TO_STRING2(...)進行檢查替換,生成標記a_PARAM(ADDPARAM(1))
(2) 對TO_STRING1的參數a_PARAM(ADDPARAM(1))進行檢查替換,生成標記a_PARAM(INT_1)
(3) 對T的參數a_PARAM(INT_1)進行檢查替換,生成字符串"a_PARAM(INT_1)"
*/

Ex. 3
#include <stdio.h>

int ival = 0;
#define A(x) printf("%d\n", ival+=1);
#define B(x) printf("%d\n", ival+=2);
#define C() printf("%d\n", ival+=3);


int main()
{
	A(B(C()));
	printf("%d\n", ival);// ?, 1

	return 0;
}

補充知識:
The C Programming Language, Second Edition
P.205 預處理

(1) 預處理器執行宏替換、條件編譯以及包含指定的文件。
(2) 以#開頭的命令行("#"前可以有空格),就是預處理器處理的對象。
(3) 這些命令行的語法獨立於語言的其他部分,它們可以出現在任何地方,其作用可延續到所在翻譯單元的末尾(與作用域無關)。
(4) 行邊界是有實際意義的。每一行都將單獨進行分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章