在宏中使用#

use*_*463 11 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)));
}
Run Code Online (Sandbox Code Playgroud)

产量

12

A(1,2)

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

Am_*_*ful 5

正如维基百科在C预处理器中所提到的:

##运算符(称为"令牌粘贴运算符")将两个标记连接成一个标记.

#运算符(称为"字符串化运算符")将标记转换为字符串,适当地转义任何引号或反斜杠.

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

您不能将宏参数与其他文本组合在一起并将其全部字符串化.但是,您可以编写一系列相邻的字符串常量和字符串化参数:C编译器然后将所有相邻的字符串常量组合成一个长字符串.

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

str (foo)  // outputs "foo"
xstr (foo) // outputs "4"
Run Code Online (Sandbox Code Playgroud)

另外,来自C-FAQ问题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 . 
Run Code Online (Sandbox Code Playgroud)

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

macro-replacement of B(A(1,2)) 
= stringification of A(1,2) 
= A(1,2).
Run Code Online (Sandbox Code Playgroud)


小智 5

这里的混淆来自一个简单的规则。

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

你的第一个案例

C(A(1,2))
Run Code Online (Sandbox Code Playgroud)

预处理器首先应用C(a)定义为B(a). 定义中没有###与参数相邻(根本没有B(a)),因此预处理器必须解析参数中的宏:

A(1,2)
Run Code Online (Sandbox Code Playgroud)

A(a,b)is的定义a##b计算为12.

在对宏参数中的C(a)宏求值后,C 宏变为:

C(12)
Run Code Online (Sandbox Code Playgroud)

预处理器现在解析C(a)宏,根据它的定义变成

B(12)
Run Code Online (Sandbox Code Playgroud)

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

"12"
Run Code Online (Sandbox Code Playgroud)

你的第二个案例

B(A(1,2))
Run Code Online (Sandbox Code Playgroud)

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

"A(1,2)"
Run Code Online (Sandbox Code Playgroud)

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

"A(1,2)"
Run Code Online (Sandbox Code Playgroud)