使用预处理器进行字符串连接

Posted

技术标签:

【中文标题】使用预处理器进行字符串连接【英文标题】:String concatenation using preprocessor 【发布时间】:2011-02-24 14:55:06 【问题描述】:

是否可以在预处理期间连接字符串?

我找到了这个例子

#define H "Hello "
#define W "World!"
#define HW H W

printf(HW); // Prints "Hello World!"

但是它对我不起作用 - 当我使用 gcc -std=c99 时打印出“Hello”

UPD 这个例子现在看起来可以工作了。但是,这是c预处理器的正常特性吗?

【问题讨论】:

【参考方案1】:

连接相邻的字符串不是预处理器的特性,而是核心语言(C 和 C++)的特性。你可以写:

printf("Hello "
       " world\n");

【讨论】:

【参考方案2】:

您确实可以在预处理器中连接标记,但要小心,因为它很棘手。关键是## 运算符。如果你要把它放在代码的顶部:

#define myexample(x,y,z) int example_##x##_##y##_##z## = x##y##z 

那么基本上,它的作用是在预处理期间,它将接受对该宏的任何调用,例如:

myexample(1,2,3);

它实际上会变成

int example_1_2_3 = 123;

如果您正确使用它,这将为您在编码时提供很大的灵活性,但它并不完全适用于您尝试使用它的方式。稍微按摩一下,你就可以让它发挥作用。

您的示例的一个可能解决方案可能是:

#define H "Hello "
#define W "World!"
#define concat_and_print(a, b) cout << a << b << endl

然后做类似的事情

concat_and_print(H,W);

【讨论】:

这是一个 c++ 标准还是只是一个 gcc 功能? 在您的示例中,连接是在运行时完成的,而不是由预处理器完成的。 你应该使用int example_##x##_##y##_##z = x##y##z 吗?【参考方案3】:

来自gcc online docs:

“##”预处理运算符执行标记粘贴。扩展宏时,每个“##”运算符两侧的两个标记组合成一个标记,然后替换宏扩展中的“##”和两个原始标记。

考虑一个解释命名命令的 C 程序。可能需要一个命令表,也许是一个声明如下的结构数组:

 struct command
 
   char *name;
   void (*function) (void);
 ;

 struct command commands[] =
 
    "quit", quit_command ,
    "help", help_command ,
   ...
 ;

不必给每个命令名两次,一次在字符串常量中,一次在函数名中,这样会更简洁。将命令名称作为参数的宏可以使这变得不必要。字符串常量可以通过字符串化来创建,函数名可以通过将参数与_command 连接来创建。以下是它的完成方式:

 #define COMMAND(NAME)   #NAME, NAME ## _command 

 struct command commands[] =
 
   COMMAND (quit),
   COMMAND (help),
   ...
 ;

【讨论】:

【参考方案4】:

我只是想我会添加一个答案,引用源代码来说明为什么会这样。

C99 标准 §5.1.1.2 定义了 C 代码的翻译阶段。第 6 小节规定:

    连接相邻的字符串文字标记。

同样,在 C++ 标准 (ISO 14882) §2.1 中定义了翻译阶段。这里第 6 小节规定:

6 相邻的普通字符串文字标记被连接。连接相邻的宽字符串文字标记。

这就是为什么您可以简单地将字符串彼此相邻地连接起来:

printf("string"" one\n");

>> ./a.out
>> string one

问题的预处理部分只是使用 #define 预处理指令,它将标识符 (H) 替换为字符串 ("Hello ")。

【讨论】:

以上是关于使用预处理器进行字符串连接的主要内容,如果未能解决你的问题,请参考以下文章

Scala:如何进行字符串连接以避免 GC 开销问题

[Google Guava]字符串处理:连接器拆分器字符匹配器

在C宏中进行字符串化之前进行标记连接

你还不会使用Guava处理字符串? 那你还不进来学习?

MySQL数据库无法使用+号连接字符串的处理方法

无法使用连接字符串名称运行支架 dbcontext