C/C++ 宏中“##”与“#”的用法

发布于:2025-03-14 ⋅ 阅读:(10) ⋅ 点赞:(0)

在 C/C++ 宏中,### 是两个特殊的预处理操作符,用于在宏展开时对参数进行字符串化和符号连接。以下是它们的详细用法和区别:


1. # 运算符(字符串化)

作用:将宏参数转换为字符串常量(即添加双引号)。
语法#参数
示例

#include <stdio.h>

// 将参数转换为字符串
#define STRINGIFY(x) #x

int main() {
    int num = 42;
    printf("变量名: %s\n", STRINGIFY(num));  // 输出: 变量名: num
    printf("值: %d\n", num);                // 输出: 值: 42
    return 0;
}

输出

变量名: num
值: 42

应用场景

  • 在调试时输出变量名和值。
  • 动态生成字符串化的参数名称。

2. ## 运算符(符号连接)

作用:将两个符号(Token)拼接为一个新的符号。
语法符号1 ## 符号2
示例

#include <stdio.h>

// 拼接变量名
#define CONCAT(a, b) a##b

int main() {
    int num1 = 10, num2 = 20;
    int num12 = num1 + num2;

    // 通过拼接生成变量名 num12
    printf("num1 + num2 = %d\n", CONCAT(num, 12));  // 输出: num1 + num2 = 30
    return 0;
}

输出

num1 + num2 = 30

应用场景

  • 动态生成变量名或函数名(例如批量定义结构体字段)。
  • 模板化代码,减少重复代码量。

3. ### 的联合使用

示例:生成带前缀的变量名并字符串化:

#include <stdio.h>

#define VAR_NAME(pre, num) pre##num
#define PRINT_VAR(pre, num) printf("%s = %d\n", #pre #num, VAR_NAME(pre, num))

int main() {
    int var_x1 = 100, var_x2 = 200;

    PRINT_VAR(var_x, 1);  // 展开为: printf("var_x1 = %d\n", var_x1)
    PRINT_VAR(var_x, 2);  // 展开为: printf("var_x2 = %d\n", var_x2)
    return 0;
}

输出

var_x1 = 100
var_x2 = 200

4. 关键区别

特性 # 运算符 ## 运算符
作用 将参数转换为字符串常量 拼接两个符号为一个新符号
输入类型 单个宏参数 两个符号(可以是参数或字面量)
输出类型 字符串(带双引号) 符号(无引号)
常见用途 调试输出、字符串生成 动态生成变量名、模板化代码

5. 注意事项

  1. ## 的限制

    • 拼接后的符号必须是有效的标识符(如 var_1),否则会导致编译错误。
    • 不能直接拼接字符串字面量(如 "Hello" ## "World" 是非法的)。
  2. 宏展开顺序

    • 宏参数在替换前会先展开,但 ### 会阻止参数展开。若需要强制展开参数,需使用中间宏。
  3. 避免歧义

    • 使用括号避免优先级问题,例如 #define CONCAT(a, b) (a##b)

6. 进阶示例

动态生成枚举和字符串映射
#include <stdio.h>

#define DEFINE_ENUM(name) \
    enum name {           \
        name##_A,        \
        name##_B,        \
        name##_C         \
    };                    \
    const char* name##_strings[] = { #name "_A", #name "_B", #name "_C" }

DEFINE_ENUM(Color);  // 生成 enum Color 和 Color_strings

int main() {
    printf("枚举值: %d -> %s\n", Color_A, Color_strings[Color_A]);  // 输出: 0 -> Color_A
    return 0;
}

总结

  • #:将宏参数转换为字符串,用于调试输出或动态字符串生成。
  • ##:拼接符号生成新标识符,适用于模板化代码和减少重复。
  • 联合使用时,可以实现高度灵活的代码生成(如反射、枚举映射等)。
  • 注意宏展开的优先级和符号有效性,避免编译错误。