一、#define
1.簡單的define定義
1 #define MAXTIME 1000
這樣的定義看起來類似於普通的常量定義CONST,但也有着不同,因為define的定義更像是簡單的文本替換,而不是作為一個量來使用,這個問題在下面反映的尤為突出。
2.define的“函數定義”
define可以像函數那樣接受一些參數,如下
1 #define max(x,y) (x)>(y)?(x):(y);
定義就將返回兩個數中較大的那個。因為這個“函數”沒有類型檢查,就好像一個函數模板似的,當然,它絕對沒有模板那么安全就是了。可以作為一個簡單的模板來使用而已。
但是這樣做的話存在隱患,例子如下:
1 #define Add(a,b) a+b;
如果遇到如:c * Add(a,b) * d的時候就會出現問題,代數式的本意是a+b然后去和c,d相乘,但是因為使用了define,所以式子實際上變成了c*a + b*d
3.宏的單行定義
1 #define A(x) T_##x 2 #define B(x) #@x 3 #define C(x) #x
我們假設:x=1,則有:
A(1)------〉T_1
B(1)------〉'1'
C(1)------〉"1"
4.define的多行定義
define可以替代多行的代碼,例如MFC中的宏定義(非常的經典,雖然讓人看了惡心)
1 #define MACRO(arg1, arg2) do { / 2 /* declarations */ / 3 stmt1; / 4 stmt2; / 5 /* ... */ / 6 } while(0) /* (no trailing ; ) */
關鍵是要在每一個換行的時候加上一個"/"
5.在大規模的開發過程中,特別是跨平台和系統的軟件里,define 最重要的功能是條件編譯。
就是:
1 #ifdef WINDOWS 2 ...... 3 #endif 4 #ifdef LINUX 5 ...... 6 #endif
可以在編譯的時候通過#define設置編譯環境
6.如何定義宏、取消宏
1 //定義宏 2 #define [MacroName] [MacroValue] 3 //取消宏 4 #undef [MacroName] 5 //普通宏 6 #define PI (3.1415926)
1 //帶參數的宏 2 #define max(a,b) ((a)>(b)? (a),(b))
7.條件編譯
1 #ifdef XXX…(#else) … 2 #endif 3 #ifdef DV22_AUX_INPUT 4 #define AUX_MODE 3 5 #else 6 #define AUY_MODE 3 7 #endif 8 #ifndef XXX … (#else) … 9 #endif
8.頭文件(.h)可以被頭文件或C文件包含
重復包含(重復定義)
由於頭文件包含可以嵌套,那么C文件就有可能包含多次同一個頭文件,就可能出現重復定義的問題的。
通過條件編譯開關來避免重復包含(重復定義)
例如
1 #ifndef __headerfileXXX__ 2 #define __headerfileXXX__ 3 … 4 //文件內容 5 … 6 #endif
9. #define中的#、## && #@
前些一段時間在看WinCE的Code時發現在宏定義中有用到##,如下所示
1 #define GPEBLT_FUNCNAME(basename) (SCODE (GPE::*)(struct GPEBltParms *))&GPE::##basename
在#define中,標准只定義了#和##兩種操作。#用來把參數轉換成字符串,##則用來連接兩個前后兩個參數,把它們變成一個字符串。
1 #define ToString(a) #a 2 ToString( A b Cd ); //A b Cd 3 ToString( A/n b Cd ); //A 4 // b Cd 5 ToString( A/ n b Cd ); //A n b Cd
另外,在網上搜到還有一種用法:#@,把參數轉換成字符
1 #define ToChar(a) #@a 2 ToChar(a); // a 3 ToChar(ab); // b 4 ToChar(abc); // c 5 ToChar(abcd); // d 6 //ToChar(abcde); // too many characters in constant 7 ToChar(1.); // .
二、typedef
用途一:定義一種類型的別名,而不只是簡單的宏替換。可以用作同時聲明指針型的多個對象。
比如:
1 char* pa, pb; // 這多數不符合我們的意圖,它只聲明了一個指向字符變量的指針,和一個字符變量; 2 typedef char* PCHAR; 3 PCHAR pa, pb;
這種用法很有用,特別是char* pa, pb的定義,初學者往往認為是定義了兩個字符型指針,其實不是,而用typedef char* PCHAR就不會出現這樣的問題,減少了錯誤的發生。
用途二:用在舊的C代碼中,幫助struct。
以前的代碼中,聲明struct新對象時,必須要帶上struct,即形式為: struct 結構名對象名,如:
1 struct tagPOINT1 2 { 3 int x; 4 int y; 5 }; 6 struct tagPOINT1 p1;
用途三:用typedef來定義與平台無關的類型。
比如定義一個叫 REAL 的浮點類型,在目標平台一上,讓它表示最高精度的類型為:
1 typedef long double REAL;
在不支持 long double 的平台二上,改為:
1 typedef double REAL;
在連 double 都不支持的平台三上,改為:
1 typedef float REAL;
也就是說,當跨平台時,只要改下 typedef 本身就行,不用對其他源碼做任何修改。
標准庫就廣泛使用了這個技巧,比如size_t。另外,因為typedef是定義了一種類型的新別名,不是簡單的字符串替換,所以它比宏來得穩健。
這個優點在我們寫代碼的過程中可以減少不少代碼量哦!
用途四:為復雜的聲明定義一個新的簡單的別名。
方法是:在原來的聲明里逐步用別名替換一部分復雜聲明,如此循環,把帶變量名的部分留到最后替換,得到的就是原聲明的最簡化版。舉例:
1 void (*b[10]) (void (*)()); //原聲明 2 //變量名為b,先替換右邊部分括號里的,pFunParam為別名一: 3 typedef void (*pFunParam)(); 4 //再替換左邊的變量b,pFunx為別名二: 5 typedef void (*pFunx)(pFunParam); 6 //原聲明的最簡化版: 7 pFunx b[10];
理解復雜聲明可用的“右左法則”:從變量名看起,先往右,再往左,碰到一個圓括號就調轉閱讀的方向;
括號內分析完就跳出括號,還是按先右后左的順序,如此循環,直到整個聲明分析完。舉例:
1 int (*func)(int *p);
//首先找到變量名func,外面有一對圓括號,而且左邊是一個*號,這說明func是一個指針;
//然后跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*func)是一個函數,所以func是一個指向這類函數的指針,即函數指針,這類函數具有int*類型的形參,返回值類型是int。
1 int (*func[5])(int *);
//func右邊是一個[]運算符,說明func是具有5個元素的數組;
//func的左邊有一個*,說明func的元素是指針(注意這里的*不是修飾func,而是修飾func[5]的,原因是[]運算符優先級比*高,func先跟[]結合)。
//跳出這個括號,看右邊,又遇到圓括號,說明func數組的元素是函數類型的指針,它指向的函數具有int*類型的形參,返回值類型為int。
這種用法是比較復雜的,出現的頻率也不少,往往在看到這樣的用法卻不能理解,相信以上的解釋能有所幫助。
三、#define和typedef的區別
1) #define是預處理指令,在編譯預處理時進行簡單的替換,不作正確性檢查,不關含義是否正確照樣帶入,只有在編譯已被展開的源程序時才會發現可能的錯誤並報錯。例如:
#define PI 3.1415926
程序中的:area=PI*r*r 會替換為3.1415926*r*r
如果你把#define語句中的數字9 寫成字母g 預處理也照樣帶入。
2)typedef是在編譯時處理的。它在自己的作用域內給一個已經存在的類型一個別名,但是You cannot use the typedef specifier inside a function definition。
3)typedef int * int_ptr; 和 #define int_ptr int *
作用都是用int_ptr代表 int * ,但是二者不同,正如前面所說 ,#define在預處理時進行簡單的替換,而typedef不是簡單替換 ,而是采用如同定義變量的方法那樣來聲明一種類型。也就是說;
1 //refer to (xzgyb(老達摩)) 2 #define int_ptr int * 3 int_ptr a, b; //相當於int * a, b; 只是簡單的宏替換 4 5 typedef int* int_ptr; 6 int_ptr a, b; //a, b 都為指向int的指針,typedef為int* 引入了一個新的助記符
這也說明了為什么下面觀點成立
1 //QunKangLi(維護成本與程序員的創造力的平方成正比) 2 typedef int * pint ; 3 #define PINT int *
那么:
1 const pint p ;//p不可更改,但p指向的內容可更改 2 const PINT p ;//p可更改,但是p指向的內容不可更改。
pint是一種指針類型 const pint p 就是把指針給鎖住了 p不可更改
而const PINT p 是const int * p 鎖的是指針p所指的對象。
4)還應經注意到#define 不是語句 不要在行末加分號,否則 會連分號一塊置換。
補充:
#elif
等同於
#else
#if
轉載自:
https://www.cnblogs.com/Jency/articles/C_Cplusplus_define.html
https://blog.csdn.net/dexingchen/article/details/3411680
https://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html