C/C++ 的宏中#和##的作用和展开

C/C++ 的宏中,#的功能是将其后面的宏参数进行字符串化操作,简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
##连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。

凡是宏定义里有用#或##的地方宏参数是不会再展开,例如_STRI(INT_MAX)中的INT_MAX就不会被展开为2147483647。
如果想要使其中的宏参数展开,则需要多加一层中间转换宏: #define STRI(s) _STRI(s)
加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的宏就能得到对应的宏参数。

 

#include<stdio.h>
#include<iostream>
using namespace std;


#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)


#define A(x) T_##x
#define B(x) #@x
#define C(x) #x


#define WARN_IF(EXP) if(EXP) cerr << #EXP << endl;
#define paster( n ) cout << "token" << #n << " = " << n << endl;
#define _CONS(a, b) int(a##+##b)
#define _STRI(s) #s
#define STRI(s) _STRI(s)

void test_sharp_symbol()
{
    printf("%s\n", h(f(1, 2))); //输出:12
    printf("%s\n", g(f(1, 2))); //输出:f(1,2)
    printf("%s\n", h(A(1)));    // A(1)------〉T_1
    printf("%d\n", B(1));       // B(1)------〉'1'
    printf("%s\n", C(1));       // C(1)------〉"1"

    int div = 0;
    WARN_IF(div == 0);              //输出: div == 0
    paster(9);                      //输出: token9 = 9
    cout << _CONS(1+2, 2) << endl;  //输出: 3
    cout << _STRI(INT_MAX) << endl; //输出: INT_MAX
    cout << STRI(INT_MAX) << endl; // prints : 2147483647
}

 

<think>嗯,用户想了解###操作符在编程中的用法或区别。首先我得回忆一下这两个操作符通常出现在哪些编程语言里。C/C++的预处理器里有这两个操作符,对吧?#操作符用于定义中将参数字符串化,而##用于连接两个标记。 用户可能是在写的时候遇到了问题,或者看到别人的代码用了这些操作符不太明白。需要分清楚两者的不同应用场景。比如,当想要在中把参数转换成字符串常量时,用#;而需要将两个标识符连接成一个时,用##。 然后得考虑用户可能的实际应用场景。比如,在调试时打印变量名值,可能会用到#操作符。或者在需要生成唯一变量名的时候,用##来拼接标识符。这时候需要举一些例子,比如DEBUG_PRINT,或者创建结构体的工厂。 还要注意不同语言中的差异,比如在Rust里可能没有这些操作符,所以需要说明这是特定于C/C++预处理器的特性。另外,用户可能混淆了这两种操作符,导致展开错误,比如错误地使用#导致连接失败,或者用##导致意外的字符串化。 另外,用户的问题可能涉及到的高级用法,比如可变参数中使用###,或者多层展开中的注意事项。需要解释清楚操作符的作用阶段是在预处理期,而不是运行时,这有助于理解它们的行为。 可能还需要提到一些常见的错误用法,比如在##两边留空格导致连接失败,或者在不恰当的地方使用#操作符,导致语法错误。比如,在连接数字变量名时,必须正确使用##才能生成有效的标识符。 最后,确保回答结构清晰,先解释每个操作符的用途,再比较它们的区别,然后通过示例加深理解,最后总结应用场景。这样用户能逐步理解两者的不同,并知道在什么情况下使用哪种操作符。</think>在C/C++预处理器中,`##``#`是两个特殊的运算符,它们在定义中处理标识符字符串化时发挥关键作用### 一、`#`运算符(字符串化) 1. **基本功能**:将参数转换为字符串字面量 2. **语法格式**: ```c #define STRINGIZE(x) #x ``` 3. **应用示例**: ```c int num = 42; printf("%s", STRINGIZE(num)); // 输出"num" ``` 此时编译器会展开为: ```c printf("%s", "num"); ``` 4. **特殊处理**:会自动转义参数中的特殊字符(如`"`会被转换为`\"`),这与Go语言简化括号使用的设计理念形成对比[^1] ### 二、`##`运算符(标记连接) 1. **核心作用**:拼接两个预处理标记为单个标识符 2. **语法格式**: ```c #define CONCAT(a,b) a##b ``` 3. **典型应用**: ```c int var12 = 100; printf("%d", CONCAT(var,12)); // 输出100 ``` 预处理器会将其展开为: ```c printf("%d", var12); ``` 4. **注意事项**:必须生成有效标识符,否则会导致编译错误,这点与C++中类外函数声明需要完整命名空间的规则类似[^2] ### 三、关键差异对比 | 特性 | `#`运算符 | `##`运算符 | |-------------|-------------------|--------------------| | 操作对象 | 单个参数 | 两个预处理标记 | | 输出类型 | 字符串字面量 | 新标识符 | | 处理阶段 | 预处理阶段 | 预处理阶段 | | 典型应用 | 调试输出 | 工厂模式生成 | | 错误表现 | 生成无效字符串 | 生成无效标识符 | ### 四、高级应用示例 1. **动态创建结构体**: ```c #define DEFINE_STRUCT(name) struct struct_##name DEFINE_STRUCT(point) { int x; int y; }; ``` 2. **带参数的调试**: ```c #define DEBUG_LOG(var) \ printf(#var " = %d", var) ``` 3. **多重连接应用**(类似回归模型中的变量组合[^3]): ```c #define MAKE_FUNC(type,name) type type##_##name() MAKE_FUNC(int, init); // 生成 int int_init() ``` ### 五、常见错误场景 1. **错误连接数字**: ```c #define BAD_CONCAT 1##2 // 生成无效标记12(非标识符) ``` 2. **错误字符串化**: ```c #define ERR_STR #123 // 直接使用#运算符非法 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值