zoukankan      html  css  js  c++  java
  • [转]预处理定义操作符Stringizing Operator (#) ,Charizing Operator (#@), TokenPasting Operator (##)

    【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

  • 相关阅读:
    《汇编语言》- 来自底层的较量
    《深入理解java虚拟机》
    《深入理解java虚拟机》
    PHP进阶,使用交互模式进行快速测试实验?
    为什么要拒绝使用大事务进行处理任务?
    一份从0到1的java项目实践清单
    怎样在mybatis里向mysql中插入毫秒数的时间?
    mysql技能提升篇
    如何快速成长?我的java之路!
    微软职位内部推荐-Senior Development Engineer
  • 原文地址:https://www.cnblogs.com/logitechlike/p/2886801.html
Copyright © 2011-2022 走看看