在C和C++程式語言中，`**typedef**`是一個關鍵字。它用來對一個資料類型取一個別名，目的是為了使原始碼更易於閱讀和理解。它通常用於簡化宣告複雜的類型組成的結構 ，但它也常常在各種長度的整數資料型別中看到，例如`size_t`和`time_t`。

語法
--

`typedef`的語法是 : `**typedef** *typedeclaration*;`

創建 `Length` 作為 `int` 的別名 : 

```
typedef int Length;

```
創建 `PFI` 作為一個指向 "一個擁有兩個 `char *` 當作參數並回傳 `int` 的函式" 的指標的別名

```
typedef int (\*PFI)(char \*, char \*);

```

使用範例
----

### 創建一個具有特別意義的資料型別

`typedef`會被用來指定一種資料型別的意義。

例如 : 以下示範一個普通的宣告，速度及分數都被宣告為`int`

```
int current\_speed ; 
int high\_score ; 

void congratulate(int your\_score) 
{ 
 if (your\_score > high\_score) 
 ...

```
通過`typedef`來指定新的資料型別的意義:

```
typedef int km\_per\_hour ;
typedef int points ;

km\_per\_hour current\_speed ; //"km\_per\_hour" is synonymous with "int" here,
points high\_score ; //and thus, the compiler treats our new variables as integers.

void congratulate(points your\_score) {
 if (your\_score > high\_score)
 ...

```
前面兩段程式碼運作狀況一樣，但是使用`typedef`的第二段程式碼更清楚的表示了兩個變數(score和speed)，雖然資料型別都是`int`，卻是各自代表不同的意義，且他們的資料並不相容。

但請注意，其清楚的表示不同意義只是對於工程師而言，C/C++的編譯器認為兩個變數都是`int`時，並不會顯示警告或錯誤，如: 以下程式碼，使用宣告為速度的變數作為`congratulate`函式的參數 : 

```
void foo() 
{ 
 km\_per\_hour km100 = 100; 
 congratulate(km100);

```

但是，雖然在上面的程式碼中，編譯器認為"km\_per\_hour"等於`int`，但在使用前綴 `unsigned`, `long`, `signed`時，兩者並不能互換使用。

```
void foo() {
 unsigned int a; // Okay
 unsigned km\_per\_hour b; // Compiler complains
 long int c; // Okay
 long km\_per\_hour d; // Compiler complains

```

另一个例子：

```
int coxes;
int jaffa;
...
coxes++;
...
if (jaffa == 10)
...

```

現在來看以下程式碼：

```
typedef int Apple;
typedef int Orange;
Apple coxes;
Orange jaffa;
...
coxes++;
...
if (jaffa == 10)
...

```

這兩段程式碼都做同樣的一件事。第二個例子使用了 `typedef`，使其更易於了解將要進行什麼。也就是一個變數包含關於蘋果的資訊，而另一個包含關於橘子的資訊。

### 簡化宣告語法

```
struct var {
 int data1;
 int data2;
 char data3;
};

```

此處使用者定義一個資料類型 *`var`*。

像這樣建立一個 *`var`* 類型的變數，程式碼必須寫為（注意，在 C++ 中宣告一個 `struct` 時，同時也隱含了 `typedef`，C 則沒有）：

```
struct var a;

```

  

在例子的最末處加入一行語句：

```
typedef struct var newtype;

```

現在要建立類型 *`var`* 的變數時，程式碼可以寫為：

```
newtype a;

```

這樣就更容易閱讀了，因為不用再為每一個 *var* 類型的變數加上關鍵字 *`struct`*。

也可以給陣列使用 `typedef` 宣告。

```
typedef BaseType NewType [arrSize];

```
這樣就可以在宣告一個 *`BaseType`* 類型和 *`arrSize`* 大小的新陣列時，將程式碼寫為：

```
NewType array;

```

### 與陣列一起使用

`typedef`可以簡單的跟陣列一起使用。例如 : 

```
typedef char arrType[6]; // type name: arrType
 // new type: char[6]

arrType arr={1,2,3,4,5,6}; // same as: char arr[6]={1,2,3,4,5,6}

arrType \*pArr; // same as: char (\*pArr)[6];

```
在這裡，`arrType`是`char[6]`的別稱。而`arrType *pArr;`則表示`pArr`是一個指向儲存`char[6]`型別記憶體的指標。

### 與指標一起使用

可以使用typedef來定義一個新的指標型別 : 

```
typedef int \*intptr; // type name: intptr
 // new type: int\*

intptr ptr; // same as: int \*ptr

```
在上面那段程式碼中，`intptr`是一個 指標型態`int*`的 新的別名。`intptr ptr;`宣告了一個變數(`ptr`)，其資料型別是`int*`。如此一來`ptr`就是一個 可以指向一段儲存`int`資料的記憶體 的指標了。

使用`typedef`來定義一個新的指標型別有時候會造成一些困惑 : 

```
typedef int \*intptr;

intptr cliff, allen; // both cliff and allen are int\* type

intptr cliff2, \*allen2; // cliff2 is int\* type, but allen2 is int\*\* type
 // same as: intptr cliff2;
 // intptr \*allen2;

```
在上面的程式碼中,*`ntptr cliff, allen;`*表示宣告兩個變數，其資料型別是`int*`，而`intptr *allan2`則使`allen2`的型別成為`int**`

### 與結構指標一起使用

`Typedef`可以跟結構體指標一起使用。如下 : 

```
struct Node {
 int data;
 struct Node \*nextptr;
};

```
使用`typedef`可以改寫成如下 : 

```
typedef struct Node Node;
struct Node {
 int data;
 Node \*nextptr;
};

```
在C語言中，可以在一行中宣告複數的變數，不管其是不是指標。不管如何，如果你要宣告指標，必須在每個變數前面加上星號。

在下面的程式碼中，工程師可能會以為`errptr`是一個指標，這個可能會引起一些錯誤。

```
struct Node \*startptr, \*endptr, \*curptr, \*prevptr, errptr, \*refptr;

```
如果你用`typedef`定義一個`Node *`，這可以保證所有的變數都是一個指向一個`structure type`的指標。

```
typedef struct Node\* NodePtr;
...
NodePtr startptr, endptr, curptr, prevptr, errptr, refptr;

```

### 與函式指標一起使用

先看看以下這段尚未使用`typedef`的程式碼：

```
int do\_math(float arg1, int arg2) {
 return arg2;
}

int call\_a\_func(int (\*call\_this)(float, int)) {
 int output = call\_this(5.5, 7);
 return output;
}

int final\_result = call\_a\_func(&do\_math);

```
這段程式碼可以被改寫成如下：

```
typedef int (\*MathFunc)(float, int);

int do\_math(float arg1, int arg2) {
 return arg2;
}

int call\_a\_func(MathFunc call\_this) {
 int output = call\_this(5.5, 7);
 return output;
}

int final\_result = call\_a\_func(&do\_math);

```
在這裡，`MathFunc`是一個指標，指向一個回傳`int`並以一個`float`和一個`int`作為參數使用的函式。當一個函式被當作參數使用時，如果少了`typedef`它可能會變得難以了解。

以下是`signal(3)`(來自FreeBSD)的函數原型：

```
void (\*signal(int sig, void (\*func)(int)))(int);

```
上面宣告的函式相當的神祕，因為它沒有清楚的顯示它以什麼函式當作參數，或回傳了什麼資料型別。一個初心者工程師甚至可能以為它接收一個`int`作為參數，並不回傳任何東西。但它其實接收了一個`int`和一個`function pointer`作為參數，並回傳了一個`function pointer`。它可以被改寫成以下程式碼：

```
typedef void (\*sighandler\_t)(int);
sighandler\_t signal(int sig, sighandler\_t func);

```

### 用來型別轉換

`typedef`同時可以用來類型轉換。例如：

```
typedef int (\*funcptr)(double); // pointer to function taking a double returning int
funcptr x = (funcptr) NULL; // C or C++
funcptr y = funcptr(NULL); // C++ only
funcptr z = static\_cast<funcptr>(NULL); // C++ only

```
左側，`funcptr`用來宣告變數；右側，`funcptr`則用來轉換值的型態。

如果少了`typedef`，替換使用宣告語法和型別轉換語法是幾乎不能做到的。例如：

```
void \*p = NULL;
int (\*x)(double) = (int (\*)(double)) p; // This is legal
int (\*)(double) y = (int (\*)(double)) p; // Left-hand side is not legal
int (\*z)(double) = (int (\*p)(double)); // Right-hand side is not legal

```

外部連結
----

* Cprogramming.com（页面存档备份，存于） - 詳細的討論

參照
--

* 抽象資料型別
* C syntax

1. ↑ . cppreference.com.  [18 June 2016]. （原始内容存档于2018-03-10）.