宏中的#和##

时间:2010-12-06 09:36:54

标签: c c-preprocessor stringification

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

为什么会这样?

3 个答案:

答案 0 :(得分:25)

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

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

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

答案 1 :(得分:19)

在类似函数的宏中出现参数,除非它是###的操作数,在替换它之前会被展开,并重新扫描整个以进一步扩展。由于g的参数 #的操作数,因此参数不会展开,而是立即进行字符串化("f(1,2)")。由于h的参数不是<{1}}和#的操作数,因此首先展开参数(##),然后替换12 {1}}),然后重新扫描并进一步扩展(g(12))。

答案 2 :(得分:11)

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

Argument Prescan

  

宏参数在 之前完全是宏扩展   替换为宏体,除非它们 字符串化 粘贴   与其他令牌。替换后,整个宏体,   包括替换参数在内,将再次扫描 以查找宏   扩大。结果是参数被扫描两次以进行扩展   宏调用它们。

Stringification

  

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

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.