46
  #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$

为什么会这样?

4

3 回答 3

40

在类似函数的宏中出现的参数,除非它是#or的操作数,否则##在替换它并重新扫描整体以进行进一步扩展之前先对其进行扩展。因为g的参数的操作数#,所以参数没有展开,而是立即字符串化 ( "f(1,2)")。因为h的参数不是#nor的操作数##,所以参数首先展开 ( 12),然后替换 ( g(12)),然后重新扫描并进一步展开 ( "12")。

于 2010-12-06T17:06:14.733 回答
39

因为这就是预处理器的工作方式。

单个 '#' 将从给定参数创建一个字符串,无论该参数包含什么,而双 '##' 将通过连接参数创建一个新标记。

gcc -E如果您想更好地了解如何评估宏,请尝试查看预处理的输出(例如使用)。

于 2010-12-06T10:18:31.330 回答
27

以下是与您的问题相关的一些概念:

参数预扫描

宏参数在被替换为宏体之前完全被宏扩展,除非它们被字符串化或 与其他标记一起粘贴。替换后,将再次扫描整个宏体,包括被替换的参数,以查找要扩展的宏。结果是参数被扫描两次以扩展其中的宏调用。

串化

当宏参数与前导 '#' 一起使用时,预处理器将其替换为实际参数的文字文本,并转换为字符串常量

#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.
于 2016-09-11T07:50:01.213 回答