[英]# and ## in macros
#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$
为什么会这样?
在类似函数的宏中出现的参数,除非它是#
或##
的操作数,否则会在替换它之前进行扩展,并重新扫描整个以进行进一步扩展。 因为g
的参数是#
的操作数,所以参数没有扩展而是立即字符串化( "f(1,2)"
)。 因为h
的参数既不是#
也不是##
的操作数,参数首先被扩展( 12
),然后被替换( g(12)
),然后重新扫描并进一步扩展( "12"
)。
因为这就是预处理器的工作方式。
单个 '#' 将从给定参数创建一个字符串,无论该参数包含什么,而双 '##' 将通过连接参数创建一个新标记。
如果您想更好地了解宏的评估方式,请尝试查看预处理的输出(例如使用gcc -E
)。
以下是与您的问题相关的一些概念:
宏参数在被替换为宏体之前是完全宏扩展的,除非它们被字符串化或与其他标记粘贴。 替换后,整个宏体,包括被替换的参数,将再次扫描以查找要扩展的宏。 结果是参数被扫描两次以扩展其中的宏调用。
当宏参数与前导“#”一起使用时,预处理器将其替换为实际参数的文字文本,转换为字符串常量。
#ABC => "ABC"
<---- 注意双引号,它是由字符串化过程添加的。
在扩展宏时将两个标记合并为一个通常很有用。 这称为标记粘贴或标记串联。 '##' 预处理运算符执行标记粘贴。 扩展宏时,每个“##”运算符两侧的两个标记合并为一个标记,然后替换宏扩展中的“##”和两个原始标记。
所以你的场景的详细流程是这样的:
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 as Stringification
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.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.