C++---之type的用法
轉自http://wenku.baidu.com/view/733ee308581b6bd97f19ead0.html,經過整理
typedef的語法描述
在現實生活中,資訊的概念可能是長度,數量和麵積等。在C語言中,資訊被抽象為int、float和 double等基本資料型別。從基本資料型別名稱上,不能夠看出其所代表的物理屬性,並且int、float和double為系統關鍵字,不可以修改。為 瞭解決使用者自定義資料型別名稱的需求,C語言中引入型別重定義語句typedef,可以為資料型別定義新的型別名稱,從而豐富資料型別所包含的屬性資訊。
typedef的語法描述
typedef 型別名稱 型別識別符號;
typedef為系統保留字,“型別名稱”為已知資料型別名稱,包括基本資料型別和使用者自定義資料型別,“型別識別符號”為新的型別名稱。例如:
typedef double LENGTH;
typedef unsigned int COUNT;
定義新的型別名稱之後,可像基本資料型別那樣定義變數。例如:
typedef unsigned int COUNT;
unsigned int b;
COUNT c;
typedef 的主要應用形式
typedef 的主要應用有如下的幾種形式:
1) 為基本資料型別定義新的型別名。
2) 為自定義資料型別(結構體、公用體和列舉型別)定義簡潔的型別名稱。
3) 為陣列定義簡潔的型別名稱。
4) 為指標定義簡潔的名稱。
為基本資料型別定義新的型別名
typedef unsigned int COUNT;
typedef double AREA;
此種應用的主要目的,首先是豐富資料型別中包含的屬性資訊,其次是為了系統移植的需要,稍後詳細描述。
為自定義資料型別(結構體、公用體和列舉型別)定義簡潔的型別名稱。例如:
struct Point
{
double x;
double y;
double z;
};
struct Point oPoint1={100,100,0};
struct Point oPoint2;
其中結構體struct Point為新的資料型別,在定義變數的時候均要有保留字struct,而不能像int和double那樣直接使用Point來定義變數。如果經過如下的修改,
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;
定義變數的方法可以簡化為
Point oPoint;
由於定義結構體型別有多種形式,因此可以修改如下:
typedef struct
{
double x;
double y;
double z;
} Point;
為陣列定義簡潔的型別名稱。例如,定義三個長度為5的整型陣列,
int a[10],b[10],c[10],d[10];
在C語言中,可以將長度為10的整型陣列看作為一個新的資料型別,再利用typedef為其重定義一個新的名稱,可以更加簡潔形式定義此種類型的變數,具體的處理方式如下:
typedef int INT_ARRAY_10[10];
typedef int INT_ARRAY_20[20];
INT_ARRAY_10 a,b,c,d;
INT_ARRAY_20 e;
其中INT_ARRAY_10和INT_ARRAY_20為新的型別名,10 和20 為陣列的長度。a,b,c,d均是長度為10的整型陣列,e是長度為20的整型陣列。
為指標定義簡潔的名稱。首先為資料指標定義新的名稱,例如
typedef char * STRING;
STRING csName={“Jhon”};
其次,可以為函式指標定義新的名稱,例如
typedef int (*MyFUN)(int a,intb);
其中MyFUN代表指向函式的指標型別的新名稱。例如
typedef int (*MyFUN)(int a,intb);
int Max(int a,int b);
MyFUN pMyFun;// 此處原文是MyFUN *pMyFun,編譯有誤,因為MyFUN型別本身就是指標型別。
pMyFun= Max;
使用typedef注意的問題
在使用typedef時,應當注意如下的問題:
1) typedef的目的是為已知資料型別增加一個新的名稱。因此並沒有引入新的資料型別。
2) typedef 只適於型別名稱定義,不適合變數的定義。
3) typedef 與#define具有相似的之處,但是實質不同。
提示#define AREA double 與typedef double AREA 可以達到相同的效果。但是其實質不同,#define為預編譯處理命令,主要定義常量,此常量可以為任何的字元及其組合,在編譯之前,將此常量出現的所有位置,用其代表的字元或字元組合無條件的替換,然後進行編譯。typedef是為已知資料型別增加一個新名稱,其原理與使用intdouble等保留字一致。
typedef和define具體的詳細區別
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 insidea function definition。
3)typedef int * int_ptr與 #define int_ptr int * 作用都是用int_ptr代表 int * ,但是二者不同,正如前面所說 ,#define在預處理 時進行簡單的替換,而typedef不是簡單替換 ,而是採用如同定義變數的方法那樣來宣告一種型別。也就是說;
//refer to (xzgyb(老達摩))
#define int_ptr int*
int_ptr a, b; //相當於int * a, b; 只是簡單的巨集替換
typedef int*int_ptr;
int_ptr a, b; //a,b 都為指向int的指標,typedef為int* 引入了一個新的助記符
這也說明了為什麼下面觀點成立
//QunKangLi(維護成本與程式設計師的創造力的平方成正比)
typedef int * pint;
#define PINT int *
那麼:
const pint p ;//p不可更改,但p指向的內容可更改
const PINT p ;//p可更改,但是p指向的內容不可更改。
pint是一種指標型別 const pint p 就是把指標給鎖住了 p不可更改
而const PINT p 是const int * p 鎖的是指標p所指的物件。
3)也許您已經注意到#define 不是語句 不要在行末加分號,否則會連分號一塊置換。
typedef的四個用途和兩個陷阱
用途一:
定義一種型別的別名,而不只是簡單的巨集替換。可以用作同時宣告指標型的多個物件。比如:
char* pa, pb; // 這多數不符合我們的意圖,它只聲明瞭一個指向字元變數的指標,
// 和一個字元變數;
以下則可行:
typedef char* PCHAR; // 一般用大寫
PCHAR pa, pb; // 可行,同時聲明瞭兩個指向字元變數的指標
雖然:
char *pa, *pb;
也可行,但相對來說沒有用typedef的形式直觀,尤其在需要大量指標的地方,typedef的方式更省事。
用途二:用在舊的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的這種用途二不是很大,但是理解了它,對掌握以前的舊程式碼還是有幫助的,畢竟我們在專案中有可能會遇到較早些年代遺留下來的程式碼。
用途三:
用typedef來定義與平臺無關的型別。 比如定義一個叫 REAL 的浮點型別,在目標平臺一上,讓它表示最高精度的型別為:
typedef long double REAL;
在不支援 longdouble 的平臺二上,改為:
typedef double REAL;
在連 double都不支援的平臺三上,改為:
typedef float REAL; 也就是說,當跨平臺時,只要改下typedef 本身就行,不用對其他原始碼做任何修改。
標準庫就廣泛使用了這個技巧,比如size_t。
另外,因為typedef是定義了一種型別的新別名,不是簡單的字串替換,所以它比巨集來得穩健(雖然用巨集有時也可以完成以上的用途)。
用途四:為複雜的宣告定義一個新的簡單的別名。方法是:在原來的聲明裡逐步用別名替換一部分複雜宣告,如此迴圈,把帶變數名的部分留到最後替換,得到的就是原宣告的最簡化版。舉例:
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 (*pFunParam)();
再替換左邊的變數b,pFunx為別名二:
typedef void (*pFunx)(pFunParam);
原宣告的最簡化版:
pFunx b[10];
3. 原宣告:doube(*)()(*e)[9];
變數名為e,先替換左邊部分,pFuny為別名一:
typedef double(*pFuny)();
再替換右邊的變數e,pFunParamy為別名二
typedef pFuny (*pFunParamy)[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個模式:
type (*)(....)函式指標
type (*)[]陣列指標
陷阱一:
記住,typedef是定義了一種型別的新別名,不同於巨集,它不是簡單的字串替換。比如:
先定義:
typedef char* PSTR;
然後: intmystrcmp(const PSTR, const PSTR);
const PSTR實際上相當於constchar*嗎?不是的,它實際上相當於char*const。 原因在於const給予了整個指標本身以常量性,也就是形成了常量指標char*const。 簡單來說,記住當const和typedef一起出現時,typedef不會是簡單的字串替換就行。
陷阱二:typedef在語法上是一個儲存類的關鍵字(如auto、extern、mutable、static、register等一樣),雖然它並不真正影響物件的儲存特性,如:
typedef static int INT2; //不可行
編譯將失敗,會提示“指定了一個以上的儲存類”。
typedef 定義函式指標
關於C++中函式指標的使用(包含對typedef用法的討論)
(一)簡單的函式指標的應用
//形式1:返回型別(*函式名)(引數表)
char(*pFun)(int);
char glFun(inta){ return;}
void main()
{
pFun = glFun;
(*pFun)(2);
}
第一行定義了一個指標變數pFun。首先我們根據前面提到的“形式1”認識到它是一個指向某種函式的指標,這種函式引數是一個int型,返回值是char型別。只有第一句我們還無法使用這個指標,因為我們還未對它進行賦值。
第二行定義了一個函式glFun()。該函式正好是一個以int為引數返回char的函式。我們要從指標的層次上理解函式——函式的函式名實際上就是一個指標,函式名指向該函式的程式碼在記憶體中的首地址。
然後就是可愛的main()函數了,它的第一句您應該看得懂了——它將函式glFun的地址賦值給變數pFun。main()函式的第二句中“*pFun”顯然是取pFun所指向地址的內容,當然也就是取出了函式glFun()的內容,然後給定引數為2。
(二)使用typedef更直觀更方便
//形式2:typedef 返回型別(*新型別)(引數表)
typedef char (*PTRFUN)(int);
PTRFUN pFun;
charglFun(int a){ return;}
voidmain()
{
pFun = glFun;
(*pFun)(2);
}
typedef的功能是定義新的型別。第一句就是定義了一種PTRFUN的型別,並定義這種型別為指向某種函式的指標,這種函式以一個int為引數並返回char型別。後面就可以像使用int,char一樣使用PTRFUN了。
第二行的程式碼便使用這個新型別定義了變數pFun,此時就可以像使用形式1一樣使用這個變量了。
三)在C++類中使用函式指標。
//形式3:typedef 返回型別(類名::*新型別)(引數表)
class CA
{
public:
char lcFun(int a){ return; }
};
CA ca;
typedef char (CA::*PTRFUN)(int);
PTRFUN pFun;
void main()
{
pFun = CA::lcFun;
ca.(*pFun)(2);
}
在這裡,指標的定義與使用都加上了“類限制”或“物件”,用來指明指標指向的函式是那個類的。這裡的類物件也可以是使用new得到的。比如:
CA *pca =new CA;
pca->(*pFun)(2);
delete pca;
而且這個類物件指標可以是類內部成員變數,你甚至可以使用this指標。比如:
類CA有成員變數PTRFUN m_pfun;
void CA::lcFun2()
{
(this->*m_pFun)(2);
}
一句話,使用類成員函式指標必須有“->*”或“.*”的呼叫。
C語言基礎之typedef的問題
基本解釋
typedef為C語言的關鍵字,作用是為一種資料型別定義一個新名字。這裡的資料型別包括內部資料型別(int,char等)和自定義的資料型別(strUCt等)。
在程式設計中使用typedef目的一般有兩個,一個是給變數一個易記且意義明確的新名字,另一個是簡化一些比較複雜的型別宣告。
至於typedef有什麼微妙之處,請你接著看下面對幾個問題的具體闡述。
typedef &結構的問題
當用下面的程式碼定義一個結構時,編譯器報了一個錯誤,為什麼呢?莫非C語言不允許在結構中包含指向它自己的指標嗎?請你先猜想一下,然後看下文說明:
typedef struct tagNode
{
char*pItem;
pNode pNext;
} *pNode;
答案與分析:
typedef與結構結合使用
typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
這語句實際上完成兩個操作:
1) 定義一個新的結構型別
struct tagMyStruct
{
int iNum;
long lLength;
};
分析:tagMyStruct稱為“tag”,即“標籤”,實際上是一個臨時名字,struct 關鍵字和tagMyStruct一起,構成了這個結構型別,不論是否有typedef,這個結構都存在。
我們可以用struct tagMyStruct varName來定義變數,但要注意,使用tagMyStruct varName來定義變數是不對的,因為struct 和tagMyStruct合在一起才能表示一個結構型別。
2) typedef為這個新的結構起了一個名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
因此,MyStruct實際上相當於struct tagMyStruct,我們可以使用MyStruct varName來定義變數。 C語言當然允許在結構中包含指向它自己的指標,我們可以在建立連結串列等資料結構的實現上看到無數這樣的例子,上述程式碼的根本問題在於typedef的應用。
根據我們上面的闡述可以知道:新結構建立的過程中遇到了pNext域的宣告,型別是pNode,要知道pNode表示的是型別的新名字,那麼在型別本身還沒有建立完成的時候,這個型別的新名字也還不存在,也就是說這個時候編譯器根本不認識pNode。
解決這個問題的方法有多種:
1)、
typedef struct tagNode
{
char*pItem;
struct tagNode *pNext;
} *pNode;
2)、
typedef struct tagNode *pNode;
struct tagNode
{
char*pItem;
pNode pNext;
};
注意:在這個例子中,你用typedef給一個還未完全宣告的型別起新名字。C語言編譯器支援這種做法。
3)、規範做法:
struct tagNode
{
char*pItem;
struct tagNode *pNext;
};
typedef struct tagNode *pNode;
typedef &#define的問題
有下面兩種定義pStr資料型別的方法,兩者有什麼不同?哪一種更好一點?
typedef char *pStr;
#define pStr char *;
答案與分析:
通常講,typedef要比#define要好,特別是在有指標的場合。請看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
在上述的變數定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們所預期的指標變數,根本原因就在於#define只是簡單的字串替換而typedef則是為一個型別起新名字。
#define用法例子:
#define f(x) x*x
main( )
{
int a=6,b=2,c;
c=f(a)/ f(b);
printf("%d\n",c);
}
以下程式的輸出結果是: 36。
因為如此原因,在許多C語言程式設計規範中提到使用#define定義時,如果定義中包含表示式,必須使用括號,則上述定義應該如下定義才對:
#definef(x) (x*x)
當然,如果你使用typedef就沒有這樣的問題。
typedef &#define的另一例
下面的程式碼中編譯器會報一個錯誤,你知道是哪個語句錯了嗎?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
答案與分析:
是p2++出錯了。這個問題再一次提醒我們:typedef和#define不同,它不是簡單的文字替換。上述程式碼中const pStr p2並不等於const char * p2。const pStr p2和const long x本質上沒有區別,都是對變數進行只讀限制,只不過此處變數p2的資料型別是我們自己定義的而不是系統固有型別而已。因此,constpStr p2的含義是:限定資料型別為char *的變數p2為只讀,因此p2++錯誤。 (注:關於const的限定內容問題,在本系列第二篇有詳細講解)。
#define與typedef引申談
1) #define巨集定義有一個特別的長處:可以使用 #ifdef,#ifndef等來進行邏輯判斷,還可以使用#undef來取消定義。
2) typedef也有一個特別的長處:它符合範圍規則,使用typedef定義的變數型別其作用範圍限制在所定義的函式或者檔案內(取決於此變數定義的位置),而巨集定義則沒有這種特性。
typedef & 複雜的變數宣告
在程式設計實踐中,尤其是看別人程式碼的時候,常常會遇到比較複雜的變數宣告,使用typedef作簡化自有其價值,比如:
下面是三個變數的宣告,我想使用typdef分別給它們定義一個別名,請問該如何做?
>1:int*(*a[5])(int, char*);
>2:void(*b[10]) (void (*)());
>3.doube(*)() (*pa)[9];
答案與分析:
對複雜變數建立一個類型別名的方法很簡單,你只要在傳統的變數宣告表示式裡用型別名替代變數名,然後把關鍵字typedef加在該語句的開頭就行了。
>1:int*(*a[5])(int, char*);
//pFun是我們建的一個類型別名
typedef int *(*pFun)(int, char*);
//使用定義的新型別來宣告物件,等價於int*(*a[5])(int, char*);
pFun a[5];
>2:void(*b[10]) (void (*)());
//首先為上面表示式藍色部分宣告一個新型別
typedef void (*pFunParam)();
//整體宣告一個新型別
typedef void (*pFun)(pFunParam);
//使用定義的新型別來宣告物件,等價於void(*b[10]) (void (*)());
pFun b[10];
>3. doube(*)() (*pa)[9];
//首先為上面表示式藍色部分宣告一個新型別
typedef double(*pFun)();
//整體宣告一個新型別
typedef pFun (*pFunParam)[9];
//使用定義的新型別來宣告物件,等價於doube(*)()(*pa)[9];
pFunParam pa;