C / C ++宏字符串串联


121
#define STR1      "s"
#define STR2      "1"
#define STR3      STR1 ## STR2

是否可以串联STR3 ==“ s1”?您可以通过将args传递给另一个Macro函数来实现。但是有直接的方法吗?


不应该是#define STR3 STR1 ## STR2
Shrinidhi

也不应该是因为将STR3定义为预处理令牌STR1STR2。并且将args传递给另一个宏函数也无济于事,因为不能将字符串文字粘贴在一起-“ s”“ 1”不是有效的标记。
Jim Balter

Answers:


157

如果它们都是字符串,则可以执行以下操作:

#define STR3 STR1 STR2

预处理器会自动连接相邻的字符串。

编辑:

如下所述,连接不是由预处理程序而是由编译器进行的。


17
从技术上讲,字符串连接是在语言级别完成的。
马丁·约克

47
预处理器不执行任何操作。正是将C语言视为相邻字符串文字,就像它们是单个字符串文字一样。
Jim Balter

7
它不仅仅是一种技术性-您无法连接L"a""b"获取L"ab",但可以连接L"a"L"b"获取L"ab"
MSalters 2011年

115

您不需要那种用于字符串文字的解决方案,因为它们是在语言级别上串联的,而且因为“ s”“ 1”不是有效的预处理器标记,因此无论如何都不会起作用。

[编辑:针对不幸的是,下面的错误“ Just for the record”评论遭到了多次投票,我将重申上面的声明,并观察程序片段

#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")

从gcc的预处理阶段生成此错误消息:错误:粘贴“” s“”和“” 1“”没有给出有效的预处理令牌

]

但是,对于常规令牌粘贴,请尝试以下操作:

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

然后,例如PPCAT_NX(s, 1)PPCAT(s, 1)产生标识符s1,除非s被定义为宏,在这种情况下PPCAT(s, 1)产生<macro value of s>1

继续主题的是这些宏:

/*
 * Turn A into a string literal without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define STRINGIZE_NX(A) #A

/*
 * Turn A into a string literal after macro-expanding it.
 */
#define STRINGIZE(A) STRINGIZE_NX(A)

然后,

#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"

相比之下,

STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"

#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"

8
仅作记录,"s""1"在C(和C ++)中有效。它们是编译器将自己连接并威胁为一个令牌的两个令牌(字符串文字)。
Shahbaz

4
您会误解我的评论和C语言。我说"s""1" isn't a valid token-是正确的;如您所说,它是两个令牌。但是将它们与##附加在一起将使它们成为一个预处理令牌,而不是两个令牌,因此编译器不会进行串联,而词法分析器会拒绝它们(该语言需要诊断)。
吉姆·巴尔特

8
@ mr5仔细阅读评论。作为宏参数传递的宏名称在传递之前不会扩展。但是,它们在宏的主体中进行了扩展。因此,如果将A定义为FRED,则STRINGIZE_NX(A)扩展为“ A”,而STRINGIZE(A)扩展为STRINGIZE_NX(FRED),后者扩展为“ FRED”。
吉姆·巴尔特

1
@bharath 生成的字符串为“ PPCAT(T1,T2)” -符合预期和期望。而不是预期的“ s1” -完全没有预期。为什么我们需要额外的间接/嵌套?-阅读代码注释,以及上面我的注释以及6个投票。仅宏的主体被扩展;在宏体之外,括号之间的宏参数在传递给宏之前不会扩展。因此,STRINGIZE_NX(whatever occurs here)扩展为“在这里发生的一切”,无论任何宏定义发生在此处或在此处。
Jim Balter

1
@bharath当然,它不会显示“名称A”-A是参数名称,而不是宏(ALEX)的参数。您声称if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"-这是错误的,与您的测试完全不同。您正在努力不理解或理解正确,我不会再对您做出回应。
吉姆·巴尔特

24

提示:STRINGIZE上面的宏很酷,但是如果您犯了一个错误并且它的参数不是宏-您的名称中有错字,或者忘记#include了头文件-则编译器会很乐意将声称的宏名放入字符串,没有错误。

如果您希望to的参数STRINGIZE始终是具有正常C值的宏,则

#define STRINGIZE(A) ((A),STRINGIZE_NX(A))

将对其进行一次扩展并检查其有效性,将其丢弃,然后再次将其扩展为字符串。

我花了一会儿时间才弄清楚为什么STRINGIZE(ENOENT)最终"ENOENT"变成了"2"... 而不是... ...我没有包括errno.h


2
重要注意事项,+ 1为正确使用,运算符。:)
杰西·奇斯霍尔姆

2
字符串的内容应为有效的C表达式没有特别的原因。如果您想这样做,建议您给它起一个不同的名称,例如STRINGIZE_EXPR。
吉姆·巴尔特

这个技巧可能是孤立地起作用的。但是它阻止了编译器看到将要连接的字符串序列。(导致类似的序列,((1),"1") "." ((2),"2")而不仅仅是“ 1”“。”“ 2”)
自构

只是为了阐明自构词法是什么:使用原始STRINGIZE定义,"The value of ENOENT is " STRINGIZE(ENOENT)有效,而"The value of ENOENT is" STRINGIZE_EXPR(X)产生错误。
Jim Balter
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.