1. 程式人生 > >(轉載)typed用法總結

(轉載)typed用法總結

    在C還是C++程式碼中,typedef都使用的很多,在C程式碼中尤其是多。typedef與#define有些相似,其實是不同的,特別是在一些複雜的用法上,看了網上一些C/C++的學習者的部落格,其中有一篇關於typedef的總結還是很不錯,由於總結的很好,我就不加修改的引用過來了,加上自己的一個分析。

基本定義:

typedef為C語言的關鍵字,作用是為一種資料型別定義一個新名字。這裡的資料型別包括內部資料型別(int,char等)和自定義的資料型別(struct等)。 在程式設計中使用typedef目的一般有兩個,一個是給變數一個易記且意義明確的新名字,另一個是簡化一些比較複雜的型別宣告。

用途一:與#define的區別

typedef 行為有點像 #define 巨集,用其實際型別替代同義字。不同點是 typedef 在編譯時被解釋,因此讓編譯器來應付超越前處理器能力的文字替換。

用途二:減少錯誤

定義一種型別的別名,而不只是簡單的巨集替換。可以用作同時宣告指標型的多個物件。比如:

[cpp] view plain copy print?
  1. char* pa, pb; // 這多數不符合我們的意圖,它只聲明瞭一個指向字元變數的指標,
  2. // 和一個字元變數;
char* pa, pb; // 這多數不符合我們的意圖,它只聲明瞭一個指向字元變數的指標,
// 和一個字元變數;

以下則可行:

[cpp]
view plain copy print?
  1. typedefcharPCHAR;  
  2. PCHAR pa, pb;    
typedef char* PCHAR;
PCHAR pa, pb;  

這種用法很有用,特別是char* pa, pb的定義,初學者往往認為是定義了兩個字元型指標,其實不是,而用typedef char* PCHAR就不會出現這樣的問題,減少了錯誤的發生。

用途三:    直觀簡潔

用在舊的C程式碼中,幫助struct。以前的程式碼中,宣告struct新物件時,必須要帶上struct,即形式為: struct 結構名物件名,如:

[cpp] view plain
copy print?
  1. struct tagPOINT1  
  2.  {  
  3.     int x;  
  4.     int y;   
  5. };  
  6. struct tagPOINT1 p1;  
struct tagPOINT1
 {
    int x;
    int y; 
};
struct tagPOINT1 p1;

而在C++中,則可以直接寫:結構名物件名,即:tagPOINT1 p1;

[cpp] view plain copy print?
  1. typedefstruct tagPOINT  
  2. {  
  3.     int x;  
  4.     int y;  
  5. }POINT;  
typedef struct tagPOINT
{
    int x;
    int y;
}POINT;

POINT p1; // 這樣就比原來的方式少寫了一個struct,比較省事,尤其在大量使用的時候,或許,在C++中,typedef的這種用途二不是很大,但是理解了它,對掌握以前的舊程式碼還是有幫助的,畢竟我們在專案中有可能會遇到較早些年代遺留下來的程式碼。

用途四:平臺無關性

用typedef來定義與平臺無關的型別。

typedef 有另外一個重要的用途,那就是定義機器無關的型別,例如,你可以定義一個叫 REAL 的浮點型別,在目標機器上它可以獲得最高的精度: [cpp] view plain copy print?
  1. typedeflongdouble REAL;   
  typedef long double REAL; 
在不支援 long double 的機器上,該 typedef 看起來會是下面這樣: [cpp] view plain copy print?
  1. typedefdouble REAL;   
  typedef double REAL; 
並且,在連 double 都不支援的機器上,該 typedef 看起來會是這樣:[cpp] view plain copy print?
  1. typedeffloat REAL;   
  typedef float REAL; 

也就是說,當跨平臺時,只要改下 typedef 本身就行,不用對其他原始碼做任何修改。

標準庫就廣泛使用了這個技巧,比如size_t。另外,因為typedef是定義了一種型別的新別名,不是簡單的字串替換,所以它比巨集來得穩健。

用途五:掩飾複合型別

typedef 還可以掩飾複合型別,如指標和陣列。 

例如,你不用像下面這樣重複定義有 81 個字元元素的陣列: [cpp] view plain copy print?
  1. char line[81];  
  2. char text[81];   
  char line[81];
  char text[81]; 
定義一個 typedef,每當要用到相同型別和大小的陣列時,可以這樣: [cpp] view plain copy print?
  1. typedefchar Line[81];   
  typedef char Line[81]; 
此時Line型別即代表了具有81個元素的字元陣列,使用方法如下: [cpp] view plain copy print?
  1. Line text, secondline;  
  2. getline(text);   
  Line text, secondline;
  getline(text); 
同樣,可以象下面這樣隱藏指標語法: [cpp] view plain copy print?
  1. typedefchar * pstr;  
  2. int mystrcmp(pstr, pstr);  
  typedef char * pstr;
  int mystrcmp(pstr, pstr);
這裡將帶我們到達第一個 typedef 陷阱。標準函式 strcmp()有兩個‘ const char *’型別的引數。因此,它可能會誤導人們象下面這樣宣告 mystrcmp(): [cpp] view plain copy print?
  1. int mystrcmp(const pstr, const pstr);   
  int mystrcmp(const pstr, const pstr); 
用GNU的gcc和g++編譯器,是會出現警告的,按照順序,‘const pstr’被解釋為‘char* const‘(一個指向 char 的指標常量),兩者表達的並非同一意思。為了得到正確的型別,應當如下宣告: [cpp] view plain copy print?
  1. typedefconstchar* pstr;  
  typedef const char* pstr;

用途六:程式碼簡化

程式碼簡化。為複雜的宣告定義一個新的簡單的別名。方法是:在原來的聲明裡逐步用別名替換一部分複雜宣告,如此迴圈,把帶變數名的部分留到最後替換,得到的就是原宣告的最簡化版。舉例: 

 原宣告:

[cpp] view plain copy print?
  1. void (*b[10]) (void (*)());  
void (*b[10]) (void (*)());

變數名為b,先替換右邊部分括號裡的,pFunParam為別名

[cpp] view plain copy print?
  1. typedefvoid (*pFunParam)();  
typedef void (*pFunParam)();

再替換左邊的變數b,pFunx為別名二:

[cpp] view plain copy print?
  1. typedefvoid (*pFunx)(pFunParam);  
typedef void (*pFunx)(pFunParam);

原宣告的最簡化版:

[cpp] view plain copy print?
  1. pFunx b[10];  
pFunx b[10];

原宣告:

[cpp] view plain copy print?
  1. doube(*)() (*e)[9];  
doube(*)() (*e)[9];

變數名為e,先替換左邊部分,pFuny為別名一:

[cpp] view plain copy print?
  1. typedefdouble(*pFuny)();  
typedef double(*pFuny)();

再替換右邊的變數e,pFunParamy為別名二

[cpp] view plain copy print?
  1. typedef pFuny (*pFunParamy)[9];  
typedef pFuny (*pFunParamy)[9];

原宣告的最簡化版:

[cpp] view plain copy print?
  1. pFunParamy e;  
pFunParamy e;

理解複雜宣告可用的“右左法則”:從變數名看起,先往右,再往左,碰到一個圓括號就調轉閱讀的方向;括號內分析完就跳出括號,還是按先右後左的順序,如此迴圈,直到整個宣告分析完。舉例:

[cpp] view plain copy print?
  1. int (*func)(int *p);  
int (*func)(int *p);

首先找到變數名func,外面有一對圓括號,而且左邊是一個*號,這說明func是一個指標;然後跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*func)是一個函式,所以func是一個指向這類函式的指標,即函式指標,這類函式具有int*型別的形參,返回值型別是int。

[cpp] view plain copy print?
  1. int (*func[5])(int *);  
int (*func[5])(int *);

func右邊是一個[]運算子,說明func是具有5個元素的陣列;func的左邊有一個*,說明func的元素是指標(注意這裡的*不是修飾func,而是修飾func[5]的,原因是[]運算子優先順序比*高,func先跟[]結合)。跳出這個括號,看右邊,又遇到圓括號,說明func陣列的元素是函式型別的指標,它指向的函式具有int*型別的形參,返回值型別為int。

用途七:typedef 和儲存類關鍵字(storage class specifier) 

這種說法是不是有點令人驚訝,typedef 就像 auto,extern,mutable,static,和 register 一樣,是一個儲存類關鍵字。這並不是說 typedef 會真正影響物件的儲存特性;它只是說在語句構成上,typedef 宣告看起來象 static,extern 等型別的變數宣告。下面將帶到第二個陷阱: 

[cpp] view plain copy print?
  1. typedefregisterint FAST_COUNTER; // 錯誤
  typedef register int FAST_COUNTER; // 錯誤
  編譯通不過。問題出在你不能在宣告中有多個儲存類關鍵字。因為符號 typedef 已經佔據了儲存類關鍵字的位置,在 typedef 宣告中不能用 register(或任何其它儲存類關鍵字)。