在宏中使用#

时间:2016-09-04 14:12:37

标签: c macros preprocessor-directive

请解释一下代码

#include <stdio.h>
#define A(a,b) a##b
#define B(a) #a
#define C(a) B(a)

main()
{
 printf("%s\n",C(A(1,2)));
 printf("%s\n",B(A(1,2)));
}

输出

  

12

     

A(1,2)

我不明白,第一个printf如何评估为12? 它是不是与第二个类似,因为C宏只是B宏的包装?

4 个答案:

答案 0 :(得分:5)

正如维基百科在C-preprocessor中提到的那样:

  

##运算符(称为&#34;令牌粘贴运算符&#34;)连接   两个令牌成一个令牌。

     

#运算符(称为&#34;字符串化运算符&#34;)转换a   令牌成为字符串,适当地转义任何引号或反斜杠。

     

如果要对宏参数的扩展进行字符串化,则可以   使用两个级别的宏:

     

您无法将宏参数与其他文本和stringify结合使用   这一切都在一起。但是,您可以编写一系列相邻的字符串   常量和字符串化参数:然后C编译器将合并   所有相邻的字符串常量都成为一个长字符串。

#define xstr(s) str(s)
#define str(s) #s
#define foo 4

str (foo)  // outputs "foo"
xstr (foo) // outputs "4"

另外,from C-FAQ Question 11.17

  

事实证明,#的定义表明它应该是   立即对一个宏参数进行字符串化,而不进一步扩展它   (如果参数恰好是另一个宏的名称)。

所以,同样地,沿着这些方向前进:

you're doing C(A(1,2)), 
which would roll to C(12), // since no #, so inner argument is expanded
and then to B(12)
// [since you've done two levels of macros in the code:
// 1. from C() to B(), and then, 2. B() to #a]
= 12 . 

然而,在第一种情况下,根据B(a)的定义,只明确地完成了1级字符串化(因为它因#而立即被字符串化)

macro-replacement of B(A(1,2)) 
= stringification of A(1,2) 
= A(1,2).

答案 1 :(得分:3)

C预处理器有两个运算符#和##。 #运算符将像macro这样的函数的参数转换为带引号的字符串,其中##运算符连接两个标识符。

#define A(a,b) a##b will concatenate a with b returning ab as string.
so A(1,2) will return 12
#define B(a) #a  will return a as string
#define C(a) B(a) will call previous one and return a as string.
so C(A(1,2)) = C(12) = B(12) = 12 (as string)
B(A(1,2)) = A(1,2) because A(1,2) is taken as an argument and returned as string A(1,2) 

答案 2 :(得分:3)

这里的困惑来自一个简单的规则。

在评估宏时,预处理器首先解析传递给宏的参数中的宏。但是,作为特殊情况,如果参数是#或与##相邻,则它不会解析此类参数中的宏。这就是规则。

您的第一个案例

C(A(1,2))

预处理器首先应用C(a)宏,其定义为B(a)。定义中的参数旁边没有###(根本不在B(a)中),因此预处理器必须解析参数中的宏:

A(1,2)

A(a,b)的定义为a##b,评估为12

在评估C(a)宏的参数中的宏之后,C宏变为:

C(12)

预处理器现在解析C(a)宏,根据其定义变为

B(12)

完成此操作后,预处理器再次评估结果中的宏并应用B(a)宏,结果变为

"12"

您的第二个案例

B(A(1,2))

与第一种情况类似,预处理器首先应用B(a)宏。但是这一次,宏的定义是这样的,即参数前面是#。因此,特殊规则适用,参数内的宏评估。因此,结果立即变为:

"A(1,2)"

预处理器再次检查结果,试图找到更多要扩展的宏,但现在一切都是字符串的一部分,并且宏不会在字符串中扩展。所以最终的结果是:

"A(1,2)"

答案 3 :(得分:2)

在类似函数的宏中使用了两个运算符:

  • ##导致宏连接两个参数。
  • #会将输入有效地转换为字符串文字。

A(a,b) ##中导致a与b连接。在B(a)中,#从输入中有效地创建了一个字符串文字。因此扩展运行如下:

C(A(1,2)) -> C(12) -> B(12) -> "12"
B(A(1,2)) -> "A(1,2)"

因为对于C(A(1,2))A(1,2)部分首先被评估为12,这两个陈述并不像它们看起来那样相等。

您可以在cppreference了解更多相关信息。