天天看點

C語言拾遺-C語言關鍵字-typedeftypedef

摘要:C語言允許使用者使用 typedef 關鍵字定義自己習慣的資料類型名稱,來替代系統預設的基本類型名稱、數組類型名稱、指針類型名稱與使用者自定義的結構型名稱、共用型名稱、枚舉型名稱等。一旦使用者在程式中定義了自己的資料類型名稱,就可以在該程式中用自己的資料類型名稱來定義變量的類型、數組的類型、指針變量的類型與函數的類型等。

例如,C 語言在 C99 之前并未提供布爾類型,但我們可以使用 typedef 關鍵字來定義一個簡單的布爾類型,如下面的代碼所示:

typedef int BOOL;
#define TRUE 1
#define FALSE 0
           

定義好之後,就可以像使用基本類型資料一樣使用它了,如下面的代碼所示:

BOOL bflag=TRUE;
           

typedef

在實際使用中,typedef 的應用主要有如下4種:

1 為基本資料類型定義新的類型名

也就是說,系統預設的所有基本類型都可以利用 typedef 關鍵字來重新定義類型名,示例代碼如下所示:

typedef unsigned int COUNT;
           

而且,我們還可以使用這種方法來定義與平台無關的類型。比如,要定義一個叫 REAL 的浮點類型,在目标平台一上,讓它表示最高精度的類型,即:

typedef long double REAL;
           

在不支援 long double 的平台二上,改為:

typedef double REAL;
           

甚至還可以在連 double 都不支援的平台三上,改為:

typedef float REAL;
           

這樣,當跨平台移植程式時,我們隻需要修改一下 typedef 的定義即可,而不用對其他源代碼做任何修改。其實,标準庫中廣泛地使用了這個技巧,比如 size_t 在 VC++2010 的 crtdefs.h 檔案中的定義如下所示:

#ifndef _SIZE_T_DEFINED
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif
#define _SIZE_T_DEFINED
#endif
           

2 為自定義資料類型(結構體、共用體和枚舉類型)定義簡潔的類型名稱

以結構體為例,下面我們定義一個名為 Point 的結構體:

struct Point
{
    double x;
    double y;
    double z;
};
           

在調用這個結構體時,我們必須像下面的代碼這樣來調用這個結構體:

struct Point oPoint1={100,100,0};
struct Point oPoint2;
           

在這裡,結構體 struct Point 為新的資料類型,在定義變量的時候均要向上面的調用方法一樣有保留字 struct,而不能像 int 和 double 那樣直接使用 Point 來定義變量。現在,我們利用 typedef 定義這個結構體,如下面的代碼所示:

typedef struct tagPoint
{
    double x;
    double y;
    double z;
} Point;
           

在上面的代碼中,實際上完成了兩個操作:

1、定義了一個新的結構類型,代碼如下所示:
struct tagPoint
{
    double x;
    double y;
    double z;
} ;
           

其中,struct 關鍵字和 tagPoint 一起構成了這個結構類型,無論是否存在 typedef 關鍵字,這個結構都存在。

2、使用 typedef 為這個新的結構起了一個别名,叫 Point,即:

typedef struct tagPoint Point
           
是以,現在你就可以像 int 和 double 那樣直接使用 Point 定義變量,如下面的代碼所示:
Point oPoint1={100,100,0};
Point oPoint2;
           

為了加深對 typedef 的了解,我們再來看一個結構體例子,如下面的代碼所示:

typedef struct tagNode
{
    char *pItem;
    pNode pNext;
} *pNode;
           

從表面上看,上面的示例代碼與前面的定義方法相同,是以應該沒有什麼問題。但是編譯器卻報了一個錯誤,為什麼呢?莫非 C 語言不允許在結構中包含指向它自己的指針?

其實問題并非在于 struct 定義的本身,大家應該都知道,C 語言是允許在結構中包含指向它自己的指針的,我們可以在建立連結清單等資料結構的實作上看到很多這類例子。那問題在哪裡呢?其實,根本問題還是在于 typedef 的應用。

在上面的代碼中,新結建構立的過程中遇到了 pNext 聲明,其類型是 pNode。這裡要特别注意的是,pNode 表示的是該結構體的新别名。于是問題出現了,在結構體類型本身還沒有建立完成的時候,編譯器根本就不認識 pNode,因為這個結構體類型的新别名還不存在,是以自然就會報錯。是以,我們要做一些适當的調整,比如将結構體中的 pNext 聲明修改成如下方式:

typedef struct tagNode
{
    char *pItem;
    struct tagNode *pNext;
} *pNode;
           

或者将 struct 與 typedef 分開定義,如下面的代碼所示:

typedef struct tagNode *pNode;
struct tagNode
{
    char *pItem;
    pNode pNext;
};
           

在上面的代碼中,我們同樣使用 typedef 給一個還未完全聲明的類型 tagNode 起了一個新别名。不過,雖然 C 語言編譯器完全支援這種做法,但不推薦這樣做。建議還是使用如下規範定義方法:

struct tagNode
{
    char *pItem;
    struct tagNode *pNext;
};
typedef struct tagNode *pNode;
           

3 為數組定義簡潔的類型名稱

它的定義方法很簡單,與為基本資料類型定義新的别名方法一樣,示例代碼如下所示:

typedef int INT_ARRAY_100[100];
INT_ARRAY_100 arr;
           

4 為指針定義簡潔的名稱

對于指針,我們同樣可以使用下面的方式來定義一個新的别名:

typedef char* PCHAR;
PCHAR pa;
           

對于上面這種簡單的變量聲明,使用 typedef 來定義一個新的别名或許會感覺意義不大,但在比較複雜的變量聲明中,typedef 的優勢馬上就展現出來了,如下面的示例代碼所示:

int *(*a[5])(int,char*);
           

對于上面變量的聲明,如果我們使用 typdef 來給它定義一個别名,這會非常有意義,如下面的代碼所示:

// PFun是我們建立的一個類型别名
typedef int *(*PFun)(int,char*);
// 使用定義的新類型來聲明對象,等價于int*(*a[5])(int,char*);
PFun a[5];
           

小心使用 typedef 帶來的陷阱

接下來看一個簡單的 typedef 使用示例,如下面的代碼所示:

typedef char* PCHAR;
int strcmp(const PCHAR,const PCHAR);
           

在上面的代碼中,“const PCHAR” 是否相當于 “const char*” 呢?

答案是否定的,原因很簡單,typedef 是用來定義一種類型的新别名的,它不同于宏,不是簡單的字元串替換。是以,“const PCHAR”中的 const 給予了整個指針本身常量性,也就是形成了常量指針“char*const(一個指向char的常量指針)”。即它實際上相當于“char*const”,而不是“const char*(指向常量 char 的指針)”。當然,要想讓 const PCHAR 相當于 const char* 也很容易,如下面的代碼所示:

typedef const char* PCHAR;
int strcmp(PCHAR, PCHAR);
           

其實,無論什麼時候,隻要為指針聲明 typedef,那麼就應該在最終的 typedef 名稱中加一個 const,以使得該指針本身是常量。

還需要特别注意的是,雖然 typedef 并不真正影響對象的存儲特性,但在文法上它還是一個存儲類的關鍵字,就像 auto、extern、static 和 register 等關鍵字一樣。是以,像下面這種聲明方式是不可行的:

typedef static int INT_STATIC;
           

不可行的原因是不能聲明多個存儲類關鍵字,由于 typedef 已經占據了存儲類關鍵字的位置,是以,在 typedef 聲明中就不能夠再使用 static 或任何其他存儲類關鍵字了。當然,編譯器也會報錯,如在 VC++2010 中的報錯資訊為“無法指定多個存儲類”。

參考連結

http://www.elecfans.com/emb/577211.html

http://c.biancheng.net/view/2040.html

http://c.biancheng.net/view/298.html

https://www.dotcpp.com/course/c/101004.html

繼續閱讀