關於 typedef 的用法
因為程式裡用了很多,上網查了一下資料,覺得寫的很詳細,就貼上來了

來源一(http://zhidao.baidu.com/question/42813297.html)

Using typedef to Curb Miscreant Code

Typedef 聲明有助於創建平台無關類型,甚至能隱藏複雜和難以理解的語法。

不管怎樣,使用 typedef 能為代碼帶來意想不到的好處,通過本文你可以學習用 typedef 從而使代碼更健壯。

typedef 聲明,簡稱 typedef,為現有類型創建一個新的名字。

比如人們常常使用 typedef 來編寫更美觀和可讀的代碼。

所謂美觀,意指 typedef 能隱藏笨拙的語法構造以及平台相關的數據類型,從而增強可移植性和以及未來的可維護性。

本文下面將竭盡全力來揭示 typedef 強大功能以及如何避免一些常見的陷阱。

如何創建平台無關的數據類型,隱藏笨拙且難以理解的語法? 使用 typedefs 為現有類型創建同義字。

定義易於記憶的類型名

typedef 使用最多的地方是創建易於記憶的類型名,用它來歸檔程序員的意圖。

類型出現在所聲明的變量名字中,位於 ''typedef'' 關鍵字右邊。

例如 : typedef int size;

此聲明定義了一個 int 的同義字,名字為 size。 注意 typedef 並不創建新的類型。

它僅僅為現有類型添加一個同義字。

你可以在任何需要 int 的上下文中使用 size :

void measure(size * psz);
size array[4];
size len = file.getlength();

typedef 還可以掩飾符合類型,如指針和數組。

例如,你不用像下面這樣重複定義有 81 個字符元素的數組 :

char line[81];
char text[81];

定義一個 typedef,每當要用到相同類型和大小的數組時,可以這樣:

typedef char Line[81];
Line text, secondline;
getline(text);

同樣,可以像下面這樣隱藏指針語法:

typedef char * pstr;
int mystrcmp(pstr, pstr);

這裡將帶我們到達第一個 typedef 陷阱。

標準函數 strcmp()有兩個"const char *"類型的參數。

因此,它可能會誤導人們像下面這樣聲明 mystrcmp() :

int mystrcmp(const pstr, const pstr);

這是錯誤的,按照順序,

"const pstr" 被解釋為 "char * const" (一個指向 char 的常量指針),

而不是 "const char *" (指向常量 char 的指針)。

這個問題很容易解決:

typedef const char * cpstr;
int mystrcmp(cpstr, cpstr); // 現在是正確的

記住 :

不管什麼時候,只要為指針聲明 typedef,那麼都要在最終的 typedef 名稱中加一個 const,

以使得該指針本身是常量,而不是對象。

代碼簡化

上面討論的 typedef 行為有點像 #define 巨集,用其實際類型替代同義字。

不同點是 typedef 在編譯時被解釋,因此讓編譯器來應付超越預處理器能力的文本替換。

例如:

typedef int (*PF) (const char *, const char *);

這個聲明引入了 PF 類型作為函數指針的同義字,該函數有兩個 const char * 類型的參數以及一個 int 類型的返回值。

如果要使用下列形式的函數聲明,那麼上述這個 typedef 是不可或缺的:

PF Register(PF pf);

Register() 的參數是一個 PF 類型的回呼(回扣)函數,返回某個函數的地址,其署名與先前註冊的名字相同。

做一次深呼吸。

下面我展示一下如果不用 typedef,我們是如何實現這個聲明的 :

int (*Register (int (*pf)(const char *, const char *)))

(const char *, const char *);

很少有程序員理解它是什麼意思,更不用說這種費解的代碼所帶來的出錯風險了。

顯然,這裡使用 typedef 不是一種特權,而是一種必需。

持懷疑態度的人可能會問 : OK,有人還會寫這樣的代碼嗎?

快速瀏覽一下揭示 signal()函數的頭文件 ,一個有同樣接口的函數。

typedef 和存儲類關鍵字(storage class specifier)

這種說法是不是有點令人驚訝,typedef 就像 auto,extern,mutable,static,和 register 一樣,是一個存儲類關鍵字。

這並是說 typedef 會真正影響對象的存儲特性;

它只是說在語句構成上,typedef 聲明看起來像 static,extern 等類型的變量聲明。

下面將帶到第二個陷阱:

typedef register int FAST_COUNTER; // 錯誤

編譯通不過。

問題出在你不能在聲明中有多個存儲類關鍵字。

因為符號 typedef 已經佔據了存儲類關鍵字的位置,在 typedef 聲明中不能用 register (或任何其它存儲類關鍵字)。

促進跨平台開發

typedef 有另外一個重要的用途,那就是定義機器無關的類型,

例如,你可以定義一個叫 REAL 的浮點類型,在目標機器上它可以i獲得最高的精度:

typedef long double REAL;

在不支持 long double 的機器上,該 typedef 看起來會是下面這樣:

typedef double REAL;

並且,在連 double 都不支持的機器上,該 typedef 看起來會是這樣:、

typedef float REAL;

你不用對源代碼做任何修改,便可以在每一種平台上編譯這個使用 REAL 類型的應用程序。

唯一要改的是 typedef 本身。

在大多數情況下,甚至這個微小的變動完全都可以通過奇妙的條件編譯來自動實現。

不是嗎? 標準庫廣泛地使用 typedef 來創建這樣的平台無關類型:size_t,ptrdiff 和 fpos_t 就是其中的例子。

此外,象 std::string 和 std::ofstream 這樣的 typedef 還隱藏了長長的,難以理解的模板特化語法,例如:basic_string,allocator> 和 basic_ofstream>。


來源二
http://www.ccfans.net/bbs/dispbbs.asp?boardid=30

C語言中typedef用法

1. 基本解釋
typedef為C語言的關鍵字,作用是為一種數據類型定義一個新名字。

這裡的數據類型包括內部數據類型(int,char等)和自定義的數據類型(struct等)。

在編程中使用typedef目的一般有兩個,一個是給變量一個易記且意義明確的新名字,另一個是簡化一些比較複雜的類型聲明。

至於typedef有什麼微妙之處,請你接著看下面對幾個問題的具體闡述。

2. typedef 結構的問題

當用下面的代碼定義一個結構時,編譯器報了一個錯誤,為什麼呢?莫非C語言不允許在結構中包含指向它自己的指針嗎?

請你先猜想一下,然後看下文說明:

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

答案與分析:

1、typedef的最簡單使用

typedef long byte_4;

給已知數據類型long起個新名字,叫byte_4。


2、 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;

3. 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定義時,如果定義中包含表達式,必須使用括號,

則上述定義應該如下定義才對:

#define f(x) (x*x)

當然,如果你使用typedef就沒有這樣的問題。

4. 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的數據類型是我們自己定義的而不是系統固有類型而已。

因此,const pStr p2的含義是:限定數據類型為char *的變量p2為只讀,因此p2++錯誤。

#define與typedef引申談

1) #define 巨集定義有一個特別的長處:可以使用 #ifdef ,#ifndef等來進行邏輯判斷,還可以使用#undef來取消定義。

2) typedef也有一個特別的長處:它符合範圍規則,使用typedef定義的變量類型其作用範圍限制在所定義的函數或者文件內(取決於此變量定義的位置),而宏定義則沒有這種特性。

5. typedef & 複雜的變量聲明

在編程實踐中,尤其是看別人代碼的時候,常常會遇到比較複雜的變量聲明,使用typedef作簡化自有其價值,比如:

下面是三個變量的聲明,我想使用typdef分別給它們定義一個別名,請問該如何做?

1:int *(*a[5])(int, char*);
2:void (*b[10]) (void (*)());
3. doube(*)() (*pa)[9];

答案與分析 :

對複雜變量建立一個類型別名的方法很簡單,你只要在傳統的變量聲明表達式裡用類型名替代變量名,

然後把關鍵字typedef加在該語句的開頭就行了。

int *(*a[5])(int, char*);
//pFun是我們建的一個類型別名
typedef int *(*pFun)(int, char*);
//使用定義的新類型來聲明對象,等價於int* (*a[5])(int, char*);
pFun a[5];
void (*b[10]) (void (*)());
//首先為上面表達式藍色部分聲明一個新類型
typedef void (*pFunParam)();
//整體聲明一個新類型
typedef void (*pFun)(pFunParam);
//使用定義的新類型來聲明對象,等價於void (*b[10]) (void (*)());
pFun b[10];
doube(*)() (*pa)[9];
//首先為上面表達式藍色部分聲明一個新類型
typedef double(*pFun)();
//整體聲明一個新類型
typedef pFun (*pFunParam)[9];
//使用定義的新類型來聲明對象,等價於doube(*)() (*pa)[9];
pFunParam pa;

BB 發表在 痞客邦 PIXNET 留言(1) 人氣()


留言列表 (1)

發表留言
  • Leonard
  • Thanks for your comprehensive explanation!!!!