什么是 (字符串化)操作符?
是 C/C++ 预处理器的一个特殊操作符,称为 字符串化操作符(Stringizing Operator)。

(图片来源网络,侵删)
它的作用是:将其后面的宏参数转换成一个字符串字面量,就是把你传给宏的参数名字,用双引号 括起来,变成一个字符串。
语法和工作原理
当 出现在宏定义中,并且位于一个宏参数的前面时,预处理器会在宏展开时执行字符串化操作。
语法格式:
#define MACRO(param) #param
工作流程:

(图片来源网络,侵删)
- 在代码中调用
MACRO(some_text)。 - 预处理器找到这个宏定义。
- 它看到
#param,知道需要对参数param进行字符串化。 - 它将实际传入的参数
some_text替换掉param。 - 它对
some_text应用字符串化操作,即在其两侧加上双引号,结果变成了"some_text"。 - 用这个结果替换掉宏调用
MACRO(some_text)。
一个简单的例子:
#include <stdio.h>
// 定义一个宏,使用 # 将参数字符串化
#define MAKE_STRING(x) #x
int main() {
int age = 30;
const char* name = "Alice";
// 宏展开过程:
// printf("Value is: %d\n", age);
// 预处理器看到 #age,将其变成 "age"
// MAKE_STRING(age) 被替换为 "age"
printf("The name of the macro argument is: %s\n", MAKE_STRING(age));
// 输出: The name of the macro argument is: age
// 宏展开过程:
// printf("Value is: %s\n", name);
// 预处理器看到 #name,将其变成 "name"
// MAKE_STRING(name) 被替换为 "name"
printf("The name of the macro argument is: %s\n", MAKE_STRING(name));
// 输出: The name of the macro argument is: name
// 即使传入的是字面量,也会被字符串化
printf("The stringized value is: %s\n", MAKE_STRING(Hello World));
// 输出: The stringized value is: Hello World
return 0;
}
一个更实用的例子:生成错误信息
字符串化操作符最常见的用途之一是在调试和错误处理中自动生成包含变量名的信息。
假设我们想写一个宏,在程序断言失败时打印出是哪个变量的值不合法。
#include <stdio.h>
#include <assert.h>
// 一个调试打印宏
#define LOG(var) printf("The value of " #var " is: %d\n", var)
// 一个带变量名的断言宏
#define CHECK_POSITIVE(var) \
do { \
if ((var) <= 0) { \
printf("Error: Variable " #var " is not positive! Its value is: %d\n", (var)); \
assert(0); \
} \
} while(0)
int main() {
int x = 10;
int y = -5;
LOG(x); // 预处理器展开为: printf("The value of x is: %d\n", x);
LOG(y); // 预处理器展开为: printf("The value of y is: %d\n", y);
CHECK_POSITIVE(x); // 正常,不会进入if
// y 是负数,会触发错误信息
CHECK_POSITIVE(y);
// 预处理器展开为:
// if (y <= 0) {
// printf("Error: Variable y is not positive! Its value is: %d\n", y);
// assert(0);
// }
return 0;
}
输出:

(图片来源网络,侵删)
The value of x is: 10
The value of y is: -5
Error: Variable y is not positive! Its value is: -5
a.out: main.c:15: main: Assertion failed.
已放弃 (核心已转储)
在这个例子中,我们无需手动输入变量名 x 或 y,宏 LOG 和 CHECK_POSITIVE 自动为我们生成了包含变量名的字符串,使得调试信息更加清晰和自动化。
重要注意事项
1 它只作用于标记,不是替换后的值
字符串化操作符 作用于的是宏参数的文本标记,而不是参数最终被替换成的值,理解这一点至关重要。
例子:
#include <stdio.h>
#define PRINT_NAME(var) printf("The variable's name is: " #var "\n")
int main() {
int my_var = 123;
PRINT_NAME(my_var);
return 0;
}
输出:
The variable's name is: my_var
即使 my_var 的值是 123,#var 字符串化的是 my_var 这个名字,而不是 123,这是 操作符的核心行为。
2 与 (连接操作符)的区别
初学者经常混淆 和 ,它们是两个完全不同的预处理器操作符。
| 操作符 | 名称 | 作用 | 示例 | 宏展开后 |
|---|---|---|---|---|
| 字符串化 (Stringizing) | 将参数转换为字符串字面量 | #x |
"x" |
|
| 连接/粘贴 (Token Pasting) | 将两个标记连接成一个新标记 | x ## y |
xy |
的例子:
#include <stdio.h>
// 定义一个宏,用于连接两个标记
#define CONCAT(a, b) a ## b
int main() {
int var1 = 10;
int var2 = 20;
// CONCAT(var, 1) 会变成 var1
printf("Value of var1: %d\n", CONCAT(var, 1)); // 输出: Value of var1: 10
// CONCAT(var, 2) 会变成 var2
printf("Value of var2: %d\n", CONCAT(var, 2)); // 输出: Value of var2: 20
// 连接数字
int number = CONCAT(100, 0); // 变成 int number = 1000;
printf("Concatenated number: %d\n", number); // 输出: Concatenated number: 1000
return 0;
}
- (字符串化操作符):在宏定义中,将其后的宏参数转换成字符串,它处理的是参数的名字,而不是值。
- 主要用途:用于自动生成调试信息、错误消息,或者创建需要变量名字符串的代码。
- 与 的区别: 是“变成字符串”, 是“把两个标记粘在一起”。
- 核心要点: 操作是在预处理阶段完成的,它只关心源代码中的文本形式,不关心变量的实际运行时值。
掌握 和 这两个操作符,能让你的宏定义能力更上一层楼,写出更强大、更易维护的代码。
