1. 程式人生 > >C++---之type的用法

C++---之type的用法

C++ typedef用法詳解

轉自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;