alg*_*eks 29 c stringification c-preprocessor
#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;
}
Run Code Online (Sandbox Code Playgroud)
只需通过查看程序,"可能"期望输出,对于两个printf语句都是相同的.但是在运行程序时,你得到它:
bash$ ./a.out
12
f(1,2)
bash$
Run Code Online (Sandbox Code Playgroud)
为什么会这样?
Chr*_*fer 27
因为这是预处理器的工作方式.
单个'#'将从给定的参数创建一个字符串,无论该参数包含什么,而double'##'将通过连接参数创建一个新的标记.
gcc -E如果想要更好地理解宏的评估方式,请尝试查看预处理的输出(例如).
小智 22
在一个函数宏一个参数的发生,除非它是的操作数#或##,被代入并重新扫描整个进一步膨胀之前扩大.因为g's参数是操作数#,所以参数不会扩展,而是立即stringified("f(1,2)").因为h的参数不是的操作数#,也不##,该参数被第一膨胀(12),则取代的(g(12)),然后重新扫描并进一步膨胀发生("12").
smw*_*dia 16
以下是您问题的一些相关概念:
论证预言:
宏参数在被替换为宏体之前完全是宏扩展的,除非它们被字符串化或粘贴 到其他标记.替换后,将再次扫描整个宏体(包括替换参数),以便扩展宏.结果是参数被扫描两次以扩展其中的宏调用.
当宏参数与前导'#'一起使用时,预处理器将其替换为实际参数的文字文本,并转换为字符串常量.
在扩展宏的同时将两个令牌合并为一个通常很有用.这称为令牌粘贴或令牌连接.'##'预处理运算符执行标记粘贴.扩展宏时,每个'##'运算符两侧的两个标记组合成一个标记,然后在宏扩展中替换'##'和两个原始标记.
所以你的场景的详细过程是这样的:
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.
Run Code Online (Sandbox Code Playgroud)