【C++】预处理定义操作符Stringizing Operator (#) ,Charizing Operator (#@), Token-Pasting Operator (##)

转载自http://pppboy.blog.163.com/blog/static/30203796201011501033268/
一、开始

前几天看代码,居然出来了“##”这个东东,不知道做什么的,学C++这么长时间了,居然。。。(太不给面子了)

本着“先行先赢”的实践学习精神和“为人民服务”的奉献精神,以网上找的很多资料为参考,美美地总结一下,激励自己,启发别人。

二、Charizing Operator (#@)
1.作用

字符化操作符。只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。作用,将传的单字符参数名转换成字符,以一对单引用括起来。

2.举例

  1: // ---------------------------------------------------
  2: // 说明:预处理操作符测试
  3: // 时间:2010.12.1
  4: // 工具:VS2008
  5: // 作者:http://pppboy.blog.163.com
  6: //---------------------------------------------------
  7: 
  8: #include "stdafx.h"
  9: #include <iostream>
 10: using namespace std;
 11: 
 12: //定义#@ 的宏
 13: #define makechar(ch) #@ ch
 14: 
 15: int main(int argc, char* argv[])
 16: {
 17:   char a = makechar(b);
 18:   cout << a << endl;
 19: 
 20:   // warning C4305: “=”: 从“int”到“char”截断
 21:   a = makechar(2346);
 22:   cout << a << endl;
 23: 
 24:   // warning C4305: “=”: 从“int”到“char”截断
 25:   a = makechar(-2);
 26:   cout << a << endl;
 27: 
 28:   //error C2015: 常量中的字符太多
 29:   //a = makechar(23467);
 30: 
 31:   system("pause");
 32:   return 0;
 33: }
 34: 

输出:

b
6
2
请按任意键继续. . .
三、Stringizing Operator (#)

1.作用

把宏参数变为一个字符串。是将宏定义中的传入参数名转换成用一对双引号括起来,字符串化操作符。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。

2.举例

  1: // ---------------------------------------------------
  2: // 说明:预处理操作符测试
  3: // 时间:2010.12.4
  4: // 工具:VS2008
  5: // 作者:http://pppboy.blog.163.com
  6: //---------------------------------------------------
  7: 
  8: #include "stdafx.h"
  9: #include <iostream>
 10: using namespace std;
 11: 
 12: #define make_string(str) #str
 13: 
 14: int main(int argc, char* argv[])
 15: {
 16:   //忽略传入参数名前面和后面的空格
 17:   cout << make_string(++abcd++) << endl;
 18:   cout << make_string(  abcd  ) << endl;
 19: 
 20:   //当传入参数名间存在空格时,编译器将会自动连接各个子字符串
 21:   //用每个子字符串中只以一个空格连接,忽略其中多余一个的空格
 22:   cout << make_string(ab+++c++d) << endl;
 23:   cout << make_string(ab   c  d) << endl;
 24: 
 25:   //某些形式的传入参数名中,若存在特殊字符,编译器会自动为其添加转义字符号'/'。
 26:   // "hello world /abc 'OK" -> /"hello world //abc /'OK/"
 27:   cout << make_string("hello world /abc 'OK") << endl;
 28: 
 29:   //也会有不能解析的时候:error C2001: 常量中有换行符
 30:   //cout << make_string(abc/') << endl;
 31: 
 32:   system("pause");
 33:   return 0;
 34: }

输出:

  1: ++abcd++
  2: abcd
  3: ab+++c++d
  4: ab c d
  5: "hello world /abc 'OK"
四、Token-Pasting Operator (##)

1.作用

符号连接操作符。将宏定义的多个形参成一个实际参数名。

2.示例

  1: #include "stdafx.h"
  2: #include <iostream>
  3: using namespace std;
  4: 
  5: #define token_pasting(n) num##n
  6: #define token_pasting_space(n) num ## n
  7: int num9=99999;
  8: 
  9: int main(int argc, char* argv[])
 10: {
 11:   cout << token_pasting(9) << endl;
 12: 
 13:   //中间有空格没有影响
 14:   cout << token_pasting_space(9) << endl;
 15: 
 16:   system("pause");
 17:   return 0;
 18: }

输出:

99999
99999
五、要注意的问题

凡宏定义里有用'#'或'##'的地方宏参数是不会再展开

示例(本例来源于网络,稍有修改)

  1: #define f(a,b) a##b 
  2: #define d(a) #a //以"#"开头的,直接替换,不展开
  3: #define s(a) d(a) //非以"#"开头的,先展开,再替换
  4: 
  5: int main(int argc, char* argv[])
  6: {
  7:   //因为d宏中的参数是另外一个宏,且带##,所以作为参数的宏不展开
  8:   cout << d(f(a,b)) << endl;
  9: 
 10:   //因为s宏中的参数是另外一个宏,但不带##,所以作为参数的宏先展开
 11:   cout << s(f(a,b)) << endl;
 12: 
 13:   system("pause");
 14:   return 0;
 15: }
 16: 
 17: 

输出

f(a,b)
ab

解决办法

用'#'或'##'的地方宏参数是不会再展开,所以解决办法就是再加一层宏,就是上面例子里的

#define s(a) d(a)
六、应用特例

弄的这么复杂,为什么要用这个东西呢?肯定是有它特别的用处。

//注:下面的例子来源于:http://hi.baidu.com/sodumeng/blog

1.合并匿名变量名

  1: #define ___ANONYMOUS1(type, var, line) type var##line 
  2: #define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line) 
  3: #define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__) 
  4: //...
  5: //使用:
  6: ANONYMOUS(static int); 
  7: //...

作用:在行号(这个匿名变量前加个名字)

  1: 第一层: --> __ANONYMOUS0(static int, __LINE__); 
  2: 第二层: --> ___ANONYMOUS1(static int, _anonymous, 70); 
  3: 第三层: --> static int _anonymous70; 

2.填充结构

  1: #define FILL(a)   {a, #a}
  2: enum IDD{OPEN, CLOSE}; 
  3: typedef struct MSG{ 
  4:   IDD id; 
  5:   const char * msg; 
  6: }MSG;
  7: 
  8: //...
  9: //使用
 10: MSG _msg[] = {FILL(OPEN), FILL(CLOSE)}; 
 11: //...

相当于

  1: MSG _msg[] = {{OPEN, "OPEN"}, {CLOSE, "CLOSE"}};

3.记录文件名

  1: #define _GET_FILE_NAME(f)   #f 
  2: #define GET_FILE_NAME(f)    _GET_FILE_NAME(f) 
  3: //...
  4: //其实这个ms没有必要呀...权当学习吧...
  5: static char FILE_NAME[] = GET_FILE_NAME(__FILE__);
  6: //...

4.得到一个数值类型所对应的字符串缓冲大小

  1: #define _TYPE_BUF_SIZE(type) sizeof #type 
  2: #define TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type) 
  3: //...
  4: //嗯,这个方法比较有创意
  5: char buf[TYPE_BUF_SIZE(INT_MAX)]; 
  6: //...

相当于

  1: //--> char buf[_TYPE_BUF_SIZE(0x7fffffff)]; 
  2: //--> char buf[sizeof "0x7fffffff"]; 
  3: //这里相当于 
  4: char buf[11];

肯定还有其它的,懒的去找了,这些平时ms都用不到,不过可能写出一些很精彩的内容来。不过俺自己一般是想不出这种办法来,权当学习一下,看一些牛x源代码时也不至于太惭愧。

七、参考地址

总结时参考了很多网址,下面给出链接,引用内容所有权均为原作者所有。

我自己的内容随便修改引用:

http://hi.baidu.com/sodumeng/blog(忘了是哪一页了)

http://blog.csdn.net/waji2000/archive/2007/11/26/1902337.aspx

http://msdn.microsoft.com/en-us/library/91tt6dfs(VS.80).aspx

http://hi.baidu.com/linzhangkun/blog/item/714a61a2125fd8a5caefd0d6.html

http://blog.chinaunix.net/u1/37538/showart_523947.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值