預處理定義操作符Stringizing Operator (#) ,Charizing Operator (#@), Token-Pasting Operator (##)

一、開始

前幾天看代碼,居然出來了“##”這個東東,不知道做什麼的,學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


發佈了39 篇原創文章 · 獲贊 104 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章