天天看點

C/C++中typedef 用法typedef 用法

typedef 用法

August 23, 2015 11:07 AM

  • typedef 用法
    • 用途
      • 1 用途一
      • 2 用途二
      • 3 用途三
      • 4 用途四
    • 陷阱
      • 1 陷阱一
      • 2 陷阱二
    • typedef和define的用法差別
      • 1 typedef的用法
      • 2 define的用法
      • 3 typedef與define的差別

看了”C指針”之後, 對C語言的一些巧妙有了進一步的了解, 不讀書就是不行啊, 井底之蛙好多年~

言歸正傳, 最近發現其中對

typedef

的用法沒有給出特别好的說明(尤其是差別

typedef

define

). 在網上整理了各路大神的一些資料, 覺得清楚了好多.

typedef

用來聲明一個别名,typedef後面的文法,是一個聲明. 筆者本來認為這個概念很清楚, 但是在具體看核心代碼的時候, 漸漸和

define

的用法混淆起來. 某些教材中介紹

typedef

的時候通常會寫出如下形式:

typedef int PARA;

(注意這裡是帶

;

的,說明它至少是一條語句). 這種形式跟

#define int PARA

(這裡就沒有

;

, 隻是條預處理語句) 幾乎一樣.

(這裡插一句, 國内的某些自主教材真是 … 還是要看經典的公認的教材啊!!!).

由于長期持有這種類似的觀念, 就很難了解下面的一些聲明:

typedef int a[10];

,

typedef void (*p)(void);

, 經常以為

a[10]

void

的别名,但是這種了解給自己講都講不通啊, 于是陷入無限的困惑中. 實際上面的語句把

a

聲明為具有10個

int

元素的數組的類型别名,

p

是一種函數指針的類型别名。

雖然在功能上,

typedef

可以看作一個跟

int PARA

分離的動作,但文法上

typedef

屬于存儲類聲明說明符,是以嚴格來說,

typedef int PARA

整個是一個完整的聲明。

定義一個函數指針類型, 比如原函數是

void func(void);

. 那麼定義的函數指針類型就是

typedef void (*Fun)(void);

然後用此類型生成一個指向函數的指針:

Fun func1;

func1

擷取函數位址之後,那麼你就可以向調用原函數那樣來使用這個函數指針:

func1(void);

1. 用途

1.1 用途一

定義一種類型的别名, 而不隻是簡單的宏替換. 可以用作同時聲明指針型的多個對象。比如:

以下則可行:

typedef char* PCHAR; // 一般用大寫
PCHAR pa, pb; // 可行,同時聲明了兩個指向字元變量的指針
           

雖然:

char *pa, *pb;
           

也可行,但相對來說沒有用

typedef

的形式直覺,尤其在需要大量指針的地方,

typedef

的方式更省事。

1.2 用途二

用在舊的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

的這種用途二不是很大,但是了解了它,對掌握以前的舊代碼還是有幫助的,畢竟我們在項目中有可能會遇到較早些年代遺留下來的代碼。

1.3 用途三

typedef

來定義與平台無關的類型。

比如定義一個叫

REAL

的浮點類型,在目标平台一上,讓它表示最高精度的類型為:

typedef long double REAL;

在不支援

long double

的平台二上,改為:

typedef double REAL;

在連

double

都不支援的平台三上,改為:

typedef float REAL;

也就是說,當跨平台時,隻要改下

typedef

本身就行,不用對其他源碼做任何修改.

标準庫就廣泛使用了這個技巧,比如

size_t

.

另外, 因為

typedef

是定義了一種類型的新别名, 不是簡單的字元串替換, 是以它比宏來的穩健(雖然有時候宏也可以完成上述用途).

1.4 用途四

為複雜的聲明定義一個新的簡單的别名. 方法是: 在原來的聲明裡逐漸用别名替換一部分複雜聲明,如此循環,把帶變量名的部分留到最後替換,得到的就是原聲明的最簡化版。舉例:

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 (*pFuncParam)();

再替換左邊的變量

b

,

pFunx

為别名二:

typedef void (*pFunx)(pFunParam);

原聲明的最簡化版:

pFunx b[10];

3.

原聲明:

double (*)()(*e)[9];

變量名為

e

, 先替換左邊部分,

pFuny

為别名一:

typedef double(*pFuny)();

再替換右邊的變量

e

,

pFunParamy

為别名二:

typedef pFuny(*pFunParam)[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. 陷阱

2.1 陷阱一

記住,

typedef

是定義了一種類型的新别名, 不同于宏, 它不是說簡單的字元串替換. 比如:

先定義:

typedef char * PSTR;

然後

int mystrcmp(const PSTR, const PSTR);

const PSTR

相當于

const char *

嗎? 不是的, 它實際上相當于

char * const

. 原因在于

const

給予了整個指針本身以常量性, 也就形成來了常量指針

char * const

. (很難了解嗎? 隻要

PSTR

當做類型整體來了解, const PSTR形容這個指針類型是一個常量指針.)

簡單來說, 記住當

const

typedef

一起出現時,

typedef

不會是簡單的字元串替換就行,.

2.2 陷阱二:

typedef

在文法上是一個存儲類的關鍵字(如

auto

,

extern

,

mutable

,

static

,

register

等一樣). 雖然它并不真正影響對象的存儲特性, 但如:

typedef static int INT2; //不可行

, 編譯将會失敗, 會提示”指定了一個以上的存儲類”.

3.

typedef

#define

的用法差別

3.1

typedef

的用法

在C/C++語言中,

typedef

常用來定義一個辨別符及關鍵字的别名, 它是語言編譯過程的一部分, 但它并不實際配置設定記憶體空間, 執行個體像:

typedef int INT;

typedef int ARRAY[];

typedef (int*) pINT;
           

typedef

可以增強程式的可讀性, 以及辨別符的靈活性, 但它也有”非直覺性”等缺點.

3.2

#define

的用法

#define

為一宏定義語句, 通常用它來定義常量(包括無參量與帶參量), 以及用來實作那些”表面似和善, 背後一長串”的宏. 它本身并不在編譯過程中進行,而是在這之前(預處理過程)就已經完成了,但也是以難以發現潛在的錯誤及其它代碼維護問題,它的執行個體像:

#define INT int

#define TRUE 

#define Add(a,b) ((a)+(b));

#define Loop_10 for (int i=; i<; i++)
           

在Scott Meyer的Effective C++一書的條款1中有關于

#define

語句弊端的分析,以及好的替代方法,大家可參看。

3.3

typedef

#define

的差別

從以上的概念便也能基本清楚,

typedef

隻是為了增加可讀性而為辨別符另起的新名稱(僅僅知識一個别名), 而

#define

原本在

C

中是為了定義常量,到了C++,

const

enum

inline

的出現使它也漸漸成為了起别名的工具。有時很容易搞不清楚與

typedef

兩者到底該用哪個好,如

#define INT int

這樣的語句,用

typedef

一樣可以完成,用哪個好呢? 我主張用

typedef

,因為在早期的許多

C

編譯器中這條語句是非法的,隻是現今的編譯器又做了擴充。為了盡可能地相容,一般都遵循

#define

定義”可讀”的常量以及一些宏語句的任務,而

typedef

則常用來定義關鍵字、冗長的類型的别名。

宏定義隻是簡單的字元串代換(原地擴充),而

typedef

則不是原地擴充,它的新名字具有一定的封裝性,以緻于新命名的辨別符具有更易定義變量的功能。請看上面第一大點代碼的第三行:

typedef (int*) pINT;

以及下面這行:

#define pINT2 int*

效果相同? 實則不同! 實踐中見差别:

pINT a,b;

的效果同

int *a; int *b;

, 表示定義了兩個整型指針變量。而

pINT2 a,b;

的效果同

int *a, b;

表示定義了一個整型指針變量

a

和整型變量

b

繼續閱讀