談起C++中的巨集,我們第一個想到的應該就是“#define”,它的基本語法長得像這樣: 巨集的聲明和普通的函數聲明很像,但是兩者之間有本質的區別:C++函數在運行時(runtime)才執行代碼段;而巨集則是在預編譯時期(preprocessor)執行代碼段。下麵簡單介紹一下幾個巨集的應用。 一、考慮下麵的 ...
談起C++中的巨集,我們第一個想到的應該就是“#define”,它的基本語法長得像這樣:
1 #define macroname(para1, para2, para3, ... ,paran) macro-body
巨集的聲明和普通的函數聲明很像,但是兩者之間有本質的區別:C++函數在運行時(runtime)才執行代碼段;而巨集則是在預編譯時期(preprocessor)執行代碼段。下麵簡單介紹一下幾個巨集的應用。
一、考慮下麵的代碼段:
1 #define PLUS_ONE(x) ((x) + 1) 2 int x=PLUS_ONE(12);
>>>x=((12) + 1)
>>>x=13
這是最簡單的應用,用macro-body替換macroname;和C函數不一樣的是,巨集是沒有返回值的,本身表達式的值將作為返回值傳回。
二、考慮下麵的代碼段:
1 #define x 1+2 2 int t=x*3; 3 std::cout<<t<<'\n';
那麼輸出t的值會是什麼呢?答案也許不是你期望中的9,而是7。原因很簡單,#define的替換實質上是表達式的替換,將上述代碼里的巨集展開後將變成這樣:
int t = 1 + 2 * 3; //t = 7
這種結果顯然不是我們願意看到的,而且這種錯誤是很難察覺的,完全沒有語法錯誤和語義錯誤;不過要解決這個問題也很容易,將表達式括起來就行了:
1 #define x (1+2)
>>>int t = (1+2)*3=9
不過這種使用巨集的方法是不推薦的,因為容易引起一些不必要且難以察覺的錯誤。在C++里,我們完全可以這樣聲明x,也能達到同樣的目的:
1 const int x=3; //聲明常數推薦用const關鍵字
三、Preprocessor預先定義好的值;
__FILE__ :編譯的文件的絕對路徑;
__LINE__ :當前行號;
__TIME__ :當前時間;
__DATE__ :當前日期。
四、C語言中macro的設計初衷之一是關於內聯函數。如果我們定義一個求較大值的MAX函數:
1 #define MAX(a,b) ((a)>(b)?(a):(b))
>>>int myInt=MAX(x,y)
>>>int myInt=((x)>(y)?(x):(y)) //上一行調用的巨集將直接這樣插入到代碼段里
如果我們將MAX寫成一個一般的函數,那麼我們調用MAX將有以下兩個過程:①調用名稱為MAX的函數 ②將比較得到的值返回。顯然在這兩種方案中,macro的方案更較高效,因為它省略的函數調用的開銷,直接能得到結果(相當於inline函數的用法,這裡不展開講inline了)。
五、字元串操作函數
看如下macro定義:
1 #define MAX(a,b) ((a)>(b)?(a):(b))
這裡的參數a和b實際上都是以string的方式傳遞的,例如MAX(10,12)返回的是字元串"12",而不是整型的12。預處理器提供兩種方式處理string類型的參數傳遞。
1、stringizing operator '#':返回一個C風格的字元串;
1 #define TEST(n) std::cout << #n << " is " << (n) << std::endl
>>>int x= 6;
>>>TEST(x*2); //std::cout << "x*2" << " is " << (x*2) << std::endl
最終得到的結果是:x*2 is 12
那麼這種看似tricky的語法有什麼用呢?其實,當上述C++代碼被翻譯成機器碼時,所有和變數x相關的概念或者語句都會被“消滅”,因為變數只會存在於C++代碼層;而通過stringizing operator,讓我們以字元串的形式保存一部分C++源代碼變得可能,這可以應用於寫一些“自我診斷(糾錯)”的函數中,有機會再深入介紹吧!
2、string concatenation operator '##':顧名思義,字元串連接運算元:
1 #define TEST2(type) type ones_##type
>>>TEST2(int);
>>>int ones_type; //這個巨集的功能其實就是聲明一個任意類型的,如int的變數,名字叫做ones_type
這個沒感覺有什麼特別的用途,trick。