【问题标题】:Use of # in a macro [duplicate]在宏中使用# [重复]
【发布时间】:2017-01-12 01:41:09
【问题描述】:

请解释一下代码

#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 macros preprocessor-directive


【解决方案1】:

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) 

【讨论】:

    【解决方案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 阅读有关这些内容的更多信息。

    【讨论】:

    • 感谢@js441的回答
    【解决方案3】:

    正如维基百科中提到的C-preprocessor

    ## 运算符(称为“令牌粘贴运算符”)连接 两个令牌合为一个令牌。

    # 运算符(称为“字符串化运算符”)将一个 标记为字符串,适当地转义任何引号或反斜杠。

    如果你想字符串化宏参数的扩展,你有 使用两个级别的宏:

    您不能将宏参数与附加文本和字符串化结合使用 这一切都在一起。但是,您可以编写一系列相邻的字符串 常量和字符串化参数:然后 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).
    

    【讨论】:

    • 谢谢@Am_I_Helpful 确实是你的帮助:)
    【解决方案4】:

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

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

    您的第一个案例

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

    【讨论】:

    • 谢谢@PineForestRanch,我现在明白了。
    猜你喜欢
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 2011-09-11
    • 2020-03-04
    • 2018-02-22
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多