typedef 用法
August 23, 2015 11:07 AM
- typedef 用法
- 用途
- 1 用途一
- 2 用途二
- 3 用途三
- 4 用途四
- 陷阱
- 1 陷阱一
- 2 陷阱二
- typedef和define的用法差別
- 1 typedef的用法
- 2 define的用法
- 3 typedef與define的差別
- 用途
看了”C指針”之後, 對C語言的一些巧妙有了進一步的了解, 不讀書就是不行啊, 井底之蛙好多年~
言歸正傳, 最近發現其中對
typedef
的用法沒有給出特别好的說明(尤其是差別
typedef
和
define
). 在網上整理了各路大神的一些資料, 覺得清楚了好多.
typedef
用來聲明一個别名,typedef後面的文法,是一個聲明. 筆者本來認為這個概念很清楚, 但是在具體看核心代碼的時候, 漸漸和
define
的用法混淆起來. 某些教材中介紹
typedef
的時候通常會寫出如下形式:
typedef int PARA;
(注意這裡是帶
;
的,說明它至少是一條語句). 這種形式跟
#define int PARA
(這裡就沒有
;
, 隻是條預處理語句) 幾乎一樣.
(這裡插一句, 國内的某些自主教材真是 … 還是要看經典的公認的教材啊!!!).
由于長期持有這種類似的觀念, 就很難了解下面的一些聲明:
typedef int a[10];
,
typedef void (*p)(void);
, 經常以為
a[10]
是
void
的别名,但是這種了解給自己講都講不通啊, 于是陷入無限的困惑中. 實際上面的語句把
a
聲明為具有10個
int
元素的數組的類型别名,
p
是一種函數指針的類型别名。
雖然在功能上,
typedef
可以看作一個跟
int PARA
分離的動作,但文法上
typedef
屬于存儲類聲明說明符,是以嚴格來說,
typedef int PARA
整個是一個完整的聲明。
定義一個函數指針類型, 比如原函數是
void func(void);
. 那麼定義的函數指針類型就是
typedef void (*Fun)(void);
然後用此類型生成一個指向函數的指針:
Fun func1;
當
func1
擷取函數位址之後,那麼你就可以向調用原函數那樣來使用這個函數指針:
func1(void);
1. 用途
1.1 用途一
定義一種類型的别名, 而不隻是簡單的宏替換. 可以用作同時聲明指針型的多個對象。比如:
以下則可行:
typedef char* PCHAR; // 一般用大寫
PCHAR pa, pb; // 可行,同時聲明了兩個指向字元變量的指針
雖然:
char *pa, *pb;
也可行,但相對來說沒有用
typedef
的形式直覺,尤其在需要大量指針的地方,
typedef
的方式更省事。
1.2 用途二
用在舊的C代碼中(具體多舊沒有查),幫助
struct
。以前的代碼中,聲明
struct
新對象時,必須要帶上
struct
, 即形式為:
struct 結構名 對象名
,如:
struct tagPOINT1
{
int x;
int y;
};
struct tagPOINT1 p1;
而在C++中, 則可以直接寫:
結構名 對象名
,即:
tagPOINT1 p1;
估計某人覺得經常多寫一個
struct
太麻煩了,于是就發明了:
typedef struct tagPOINT
{
int x;
int y;
}POINT;
POINT p1; // 這樣就比原來的方式少寫了一個struct,比較省事,尤其在大量使用的時候
或許在C++中,
typedef
的這種用途二不是很大,但是了解了它,對掌握以前的舊代碼還是有幫助的,畢竟我們在項目中有可能會遇到較早些年代遺留下來的代碼。
1.3 用途三
用
typedef
來定義與平台無關的類型。
比如定義一個叫
REAL
的浮點類型,在目标平台一上,讓它表示最高精度的類型為:
typedef long double REAL;
在不支援
long double
的平台二上,改為:
typedef double REAL;
在連
double
都不支援的平台三上,改為:
typedef float REAL;
也就是說,當跨平台時,隻要改下
typedef
本身就行,不用對其他源碼做任何修改.
标準庫就廣泛使用了這個技巧,比如
size_t
.
另外, 因為
typedef
是定義了一種類型的新别名, 不是簡單的字元串替換, 是以它比宏來的穩健(雖然有時候宏也可以完成上述用途).
1.4 用途四
為複雜的聲明定義一個新的簡單的别名. 方法是: 在原來的聲明裡逐漸用别名替換一部分複雜聲明,如此循環,把帶變量名的部分留到最後替換,得到的就是原聲明的最簡化版。舉例:
1.
原聲明:
int *(*a[5])(int, char*);
變量名為
a
,直接用一個新别名
pFun
替換
a
就可以了:
typedef int *(*pFun)(int, char*);
原聲明的最簡化版:
pFun a[5];
2.
原聲明:
void (*b[10])(void (*)());
變量名為
b
, 先替換右邊部分括号裡的,
pFunParam
為别名一:
typedef void (*pFuncParam)();
再替換左邊的變量
b
,
pFunx
為别名二:
typedef void (*pFunx)(pFunParam);
原聲明的最簡化版:
pFunx b[10];
3.
原聲明:
double (*)()(*e)[9];
變量名為
e
, 先替換左邊部分,
pFuny
為别名一:
typedef double(*pFuny)();
再替換右邊的變量
e
,
pFunParamy
為别名二:
typedef pFuny(*pFunParam)[9];
原聲明的最簡化版:
pFunParamy e;
了解複雜的聲明可用”右左法則”:從變量名看起, 先往右, 再往左, 碰到一個圓括号就調轉閱讀的方向; 括号内分析完就跳出括号, 還是按先右後左的順序, 如此循環, 知道整個聲明分析完. 舉例:
int (*func)(int *p);
首先找到變量名
func
, 外面有一對圓括号, 而且左邊是一個
*
号, 這說明
func
是一個指針; 然後跳出這個圓括号, 先看右邊, 又遇到圓括号, 這說明
(*func)
是一個函數, 是以
func
是一個指向這類函數的指針, 即函數指針. 這類函數具有
int *
類型的形參, 傳回值類型是
int
.
int (*func[5])(int *);
func
右邊是一個
[]
運算符,說明
func
是具有5個元素的數組;
func
的左邊有一個
*
,說明
func
的元素是指針(注意這裡的
*
不是修飾
func
,而是修飾
func[5]
的,原因是
[]
運算符優先級比
*
高,
func
先跟
[]
結合)。跳出這個括号,看右邊,又遇到圓括号,說明
func
數組的元素是函數類型的指針,它指向的函數具有
int *
類型的形參,傳回值類型為
int
。
2. 陷阱
2.1 陷阱一
記住,
typedef
是定義了一種類型的新别名, 不同于宏, 它不是說簡單的字元串替換. 比如:
先定義:
typedef char * PSTR;
然後
int mystrcmp(const PSTR, const PSTR);
const PSTR
相當于
const char *
嗎? 不是的, 它實際上相當于
char * const
. 原因在于
const
給予了整個指針本身以常量性, 也就形成來了常量指針
char * const
. (很難了解嗎? 隻要
PSTR
當做類型整體來了解, const PSTR形容這個指針類型是一個常量指針.)
簡單來說, 記住當
const
和
typedef
一起出現時,
typedef
不會是簡單的字元串替換就行,.
2.2 陷阱二:
typedef
在文法上是一個存儲類的關鍵字(如
auto
,
extern
,
mutable
,
static
,
register
等一樣). 雖然它并不真正影響對象的存儲特性, 但如:
typedef static int INT2; //不可行
, 編譯将會失敗, 會提示”指定了一個以上的存儲類”.
3. typedef
和 #define
的用法差別
typedef
#define
3.1 typedef
的用法
typedef
在C/C++語言中,
typedef
常用來定義一個辨別符及關鍵字的别名, 它是語言編譯過程的一部分, 但它并不實際配置設定記憶體空間, 執行個體像:
typedef int INT;
typedef int ARRAY[];
typedef (int*) pINT;
typedef
可以增強程式的可讀性, 以及辨別符的靈活性, 但它也有”非直覺性”等缺點.
3.2 #define
的用法
#define
#define
為一宏定義語句, 通常用它來定義常量(包括無參量與帶參量), 以及用來實作那些”表面似和善, 背後一長串”的宏. 它本身并不在編譯過程中進行,而是在這之前(預處理過程)就已經完成了,但也是以難以發現潛在的錯誤及其它代碼維護問題,它的執行個體像:
#define INT int
#define TRUE
#define Add(a,b) ((a)+(b));
#define Loop_10 for (int i=; i<; i++)
在Scott Meyer的Effective C++一書的條款1中有關于
#define
語句弊端的分析,以及好的替代方法,大家可參看。
3.3 typedef
與 #define
的差別
typedef
#define
從以上的概念便也能基本清楚,
typedef
隻是為了增加可讀性而為辨別符另起的新名稱(僅僅知識一個别名), 而
#define
原本在
C
中是為了定義常量,到了C++,
const
、
enum
、
inline
的出現使它也漸漸成為了起别名的工具。有時很容易搞不清楚與
typedef
兩者到底該用哪個好,如
#define INT int
這樣的語句,用
typedef
一樣可以完成,用哪個好呢? 我主張用
typedef
,因為在早期的許多
C
編譯器中這條語句是非法的,隻是現今的編譯器又做了擴充。為了盡可能地相容,一般都遵循
#define
定義”可讀”的常量以及一些宏語句的任務,而
typedef
則常用來定義關鍵字、冗長的類型的别名。
宏定義隻是簡單的字元串代換(原地擴充),而
typedef
則不是原地擴充,它的新名字具有一定的封裝性,以緻于新命名的辨別符具有更易定義變量的功能。請看上面第一大點代碼的第三行:
typedef (int*) pINT;
以及下面這行:
#define pINT2 int*
效果相同? 實則不同! 實踐中見差别:
pINT a,b;
的效果同
int *a; int *b;
, 表示定義了兩個整型指針變量。而
pINT2 a,b;
的效果同
int *a, b;
表示定義了一個整型指針變量
a
和整型變量
b
。