字符串化 - 它是如何工作的?

61

我知道:

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

str(foo) 会输出 "foo",因为文本展开时首先执行字符串化操作。

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

使用xstr(foo)会输出:"4"

为什么会这样?这个过程包含哪些步骤?


1
@alk 这个链接已经变了,正确的链接是:https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing - Sujay Phadke
2个回答

65
宏展开的相关步骤如下(根据C 2011 [n1570] 6.10.3.1和C++ 1998 16.3.1):
  1. 处理以 # 或者 ## 开头的标记。
  2. 对每个参数应用宏替换。
  3. 将每个参数替换为上述宏替换的相应结果。
  4. 重新扫描以获取更多宏。
因此,对于 xstr(foo),我们有以下步骤:
  1. 替换文本 str(s) 不包含 # 或者 ##,因此什么都不会发生。
  2. 参数 foo 被替换为 4,因此就好像使用了 xstr(4)
  3. 在替换文本 str(s) 中,参数 s 被替换为 4,生成 str(4)
  4. str(4) 被重新扫描。 (产生的步骤会产生 "4"。)
请注意,str(foo) 的问题在于第2步,将 foo 替换为 4 的步骤在第1步之后,而第1步将参数更改为字符串。 在第1步中,foo 仍然是 foo;它没有被替换为 4,因此结果是 "foo"
这就是为什么要使用辅助宏的原因。 它允许我们执行第2步,然后使用另一个宏执行第1步。

3
终于明白了!读完这个答案后,我终于明白为什么它能像这样工作。虽然我的直觉一直在正确地使用它,但是我无法找到规则。stringify宏经常出现在许多地方(例如将__LINE__附加到标识符上 - 强制其被扩展而不是保留为__LINE__,在这种情况下,您将使用##而不是),或者最近需要将参数传递给GCC的_Pragma()宏(内部函数?),该宏接受带引号的字符串,因此您可能需要:#define my_pragma(arg) _Pragma(stringify("blah " #arg " blah2")) - Tomasz Gandor
#define PE(BZL,YY) printf("错误 - " #BZL ":结果:0x%08x!", YY); 正在处理:int variable1=1; PE(Test1,variable1); --> 结果输出:错误 - Test1:结果:0x1! - Joniale

16

第一种情况

  1. 评估 str(foo):用 #foo 替换 str(foo),即 "foo"

第二种情况

  1. 评估 xstr(foo): 用 str(<foo-value>) 替换 xstr(foo),即 str(4)
  2. 评估 str(4): 用 #4 替换 str(4),即 "4"

通常情况下,

预处理器会评估宏函数并展开宏变量,直到没有可评估的内容为止

如果你定义了

#define xstr(s) str(s) + 1
#define str(s) s + 1

在下面的代码中

#define foo 4

int main()
{
    std::cout << str(foo) << '\n' 
              << xstr(foo) << '\n' ;

} 

str(foo)替换为<foo-value> + 1,即4 + 1

  1. 没有更多需要替换的内容。完成。

结果为4 + 1

xstr(foo)替换为str(<foo-value>) + 1,即str(4) + 1

  1. str(4)替换为<4-value> + 1,即4 + 1
  2. 没有更多需要替换的内容。

结果为4 + 1 + 1


我还是不明白... 第一种情况:1)将foo转换为字符串 2)展开,现在我有了foo。第二种情况:1)将其转换为字符串,但没有任何内容可供转换。2)展开,现在我有了str(s)。3)将s转换为字符串,现在我有了str(s)foo。4)展开,现在我有了foo。为什么会不同? - Marco A.
@DavidKernin,那么您明白“stringify”操作是吗? 这只是一步。 - Lol4t0
@DavidKernin,这并不像你认为的那样聪明。它相当愚蠢。它只是扩展了一切。 - Lol4t0
1
我明白了,这就像一个函数堆栈.. 它会一直进行替换,直到遇到 # 然后停止替换。如果没有其他 #,则从这些“函数”返回的替换将继续进行。 - Marco A.

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