#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;
}
通过查看程序,“可能”期望输出,对于两个printf语句都是相同的。但是在运行程序时,你会得到它:
bash$ ./a.out
12
f(1,2)
bash$
为什么会这样?
答案 0 :(得分:25)
因为这是预处理器的工作方式。
单个'#'将从给定的参数创建一个字符串,无论该参数包含什么,而double'##'将通过连接参数创建一个新的标记。
如果您想更好地了解宏的评估方式,请尝试查看预处理的输出(例如使用gcc -E
)。
答案 1 :(得分:19)
在类似函数的宏中出现参数,除非它是#
或##
的操作数,在替换它之前会被展开,并重新扫描整个以进一步扩展。由于g
的参数是 #
的操作数,因此参数不会展开,而是立即进行字符串化("f(1,2)"
)。由于h
的参数不是<{1}}和#
的操作数,因此首先展开参数(##
),然后替换12
{1}}),然后重新扫描并进一步扩展(g(12)
)。
答案 2 :(得分:11)
以下是您问题的一些相关概念:
宏参数在 之前完全是宏扩展 替换为宏体,除非它们 字符串化 或 粘贴 与其他令牌。替换后,整个宏体, 包括替换参数在内,将再次扫描 以查找宏 扩大。结果是参数被扫描两次以进行扩展 宏调用它们。
当宏参数与前导'#'一起使用时,预处理器 将其替换为实际参数的文字文本,转换为 字符串常量 。
Token Pasting / Token Concatenation:
在扩展时将两个令牌合并为一个通常很有用 宏。这称为 令牌粘贴 或 令牌串联 。 '##' 预处理操作符执行令牌粘贴。当宏是 扩展后,每个'##'运算符两侧的两个标记都是 合并为一个令牌,然后替换'##'和两个 原始代币在宏观扩张中。
因此,您的方案的详细过程如下:
h(f(1,2))
-> h(12) // f(1,2) pre-expanded since there's no # or ## in macro h
-> g(12) // h expanded to g
12 // g expanded
g(f(1,2))
-> "f(1,2)" //f(1,2) is literally strigified because of the `#` in macro g. f(1,2) is NOT expanded at all.