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