双重字符串化技巧究竟是如何工作的?

93

至少有一些C预处理器允许您通过将其传递到另一个函数宏中来字符串化宏的值,而不是它的名称,以便在该宏中进行字符串化:

#define STR1(x) #x
#define STR2(x) STR1(x)
#define THE_ANSWER 42
#define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */

这里有使用案例

在GCC和Clang中,这确实有效(均使用-std=c99),但我不确定它如何在C标准术语中工作。

C99是否保证了这种行为?
如果是,C99如何保证它?
如果不是,那么从C定义到GCC定义的行为在什么时候发生?


2
如果你说“至少有一些”,这是否意味着你见过其中一个不起作用的例子?我愿意向供应商提交错误报告。 - Jens
1
@Jens:没有,我用过的每个编译器(GCC和Clang)都实现了这种行为。 - Peter Hosey
2个回答

84

是的,这是有保证的。

它起作用是因为宏的参数本身会被宏扩展,除非在宏体中出现了字符串化符号#或令牌粘贴符##。

引用自6.10.3.1/1:

... 在确定函数式宏调用的参数之后,进行参数替换。替换列表中的参数(除了被#或##预处理标记所引导的,详情见下文),在其中包含的所有宏都已经被扩展之后,将被相应的参数替换...

因此,如果您执行STR1(THE_ANSWER),则会得到"THE_ANSWER",因为STR1的参数未被宏扩展。但是,当STR2的参数被替换到STR2的定义中时,它是被宏扩展的,因此STR1得到一个参数42,结果为"42"。


22

正如Steve所指出的,这是有保障的,并且自从C89标准以来就一直保障了这一点。这是规范中宏定义中的###操作符,并要求在将宏替换到参数体之前,仅当参数体没有应用###操作符时,才会递归地展开宏。在这方面,C99与C89没有区别。


1
感谢您花时间确认它也是C89的一部分。我们中的一些人关心可移植性,包括编写可以在C89模式下编译的代码-就在几年前,gcc发布仍然默认为C89(加上gcc扩展程序公平地说),据我所知,MSVC仍然只支持C89,并且嵌入式或遗留系统有时也只有C89编译器。 C89成为了一个很好的可移植性“基础”,是一个最低公共标准,人们可以针对它来编译和运行今天实际使用的任何东西。因此,看到它被记住是非常好的。 - mtraceur

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