在宏中使用#

Use of # in a macro

请解释代码

#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宏的包装?

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

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

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

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

In 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 阅读更多相关信息。

如维基百科中所述 C-preprocessor :

The ## operator (known as the "Token Pasting Operator") concatenates two tokens into one token.

The # operator (known as the "Stringification Operator") converts a token into a string, escaping any quotes or backslashes appropriately.

If you want to stringify the expansion of a macro argument, you have to use two levels of macros:

You cannot combine a macro argument with additional text and stringify it all together. You can however write a series of adjacent string constants and stringified arguments: the C compiler will then combine all the adjacent string constants into one long string.

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

It turns out that the definition of # says that it's supposed to stringize a macro argument immediately, without further expanding it (if the argument happens to be the name of another macro).

因此,类似地,沿着这些路线前进:

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) 的定义显然只完成了一级字符串化(因为它因为#而立即被字符串化)

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

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

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

你的第一个案例

C(A(1,2))

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

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)"