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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM