确切地说,双字符串技巧是如何工作的?


84

至少某些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定义?


1
如果您说“至少一些”,是否表示您看到了一个不起作用的地方?我愿意将错误报告写给供应商。
詹斯

@詹斯:不;我没有 我使用的每个编译器(即GCC和Clang)都实现了此行为。
彼得·霍西

Answers:


80

是的,有保证。

之所以可以使用它,是因为宏的参数本身是宏扩展的,除非宏参数名称出现在带有串号#或令牌标记##的宏主体中。

6.10.3.1/1:

...确定了调用类似函数的宏的参数后,将进行参数替换。除非包含在其中的所有宏都已扩展,否则替换列表中的参数(除非在#或##预处理令牌之前或在##预处理令牌之后(请参见下文))由相应的参数替换...

因此,如果这样做,STR1(THE_ANSWER)则会得到“ THE_ANSWER”,因为STR1的参数未进行宏扩展。然而,STR2的参数当它代入STR2的定义中,其因此给出STR1的参数宏扩展42,以“42”的结果。


21

正如史蒂夫(Steve)所指出的那样,这是有保证的,并且自C89标准以来一直得到保证-这是将宏中##运算符并强制递归扩展args中的宏,然后,当且仅当主体不对参数应用##。在这方面,C99与C89相同。


+1以花时间确认它也是C89的一部分。我们中的一些人关心可移植性,包括使人们可以使用C89模式进行编译的代码可以使用-仅在几年前,gcc版本仍默认为C89(加上gcc扩展名),最后我听说MSVC仍然仅支持C89,并且嵌入式或旧系统有时也只有C89编译器。C89构成了一个很好的可移植性“底层”,这是最不常见的分母标准,人们可以以此为目标进行编译并在当今实际使用中运行。因此,很高兴看到它被记住。
mtraceur
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.