宏中的#和##是什么意思?

58
  #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个回答

49

在函数式宏中,除非出现在###的操作数中,否则函数参数的出现将在替代之前进行展开并重新扫描整个参数以进行进一步的展开。由于g的参数是#的操作数,因此该参数不会被展开,而是立即变为字符串("f(1,2)")。由于h的参数不是###的操作数,因此首先展开它的参数(12),然后进行替换(g(12)),接着重新扫描整个参数并进行进一步展开("12")。


47

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

一个'#'符号将创建一个字符串,不论该参数包含什么内容,而双'##'会通过连接参数来创建一个新的标记。

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


6
我认为问题更多地涉及到h(f(1,2))g(f(1,2))的差异,从这个意义上说,@joe-bloggs的回答更加清晰。 - Vsevolod Ganin
嗨,如果#define STR“AAA”``#define f(a)(STR#a)那么f(BBB)会是“AAA”“BBB”还是“AAA BBB”?这个单独的#能在字符串中间连接参数吗? - Guodong Hu

34

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

参数预先处理

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

字符串化

当宏参数与带有前导“#”号时,预处理器将其替换为实际参数的文字文本,转换为字符串常量

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接