typedef 是 C/C++ 中為現有類型建立別名的關鍵字,不僅能簡化複雜宣告,更是跨平台開發、程式碼可讀性提升的關鍵工具。本文系統整理其核心用法、常見陷阱、與巨集差異,並提供實戰技巧。
// 無 typedef:易誤以為 pb 也是指標
char* pa, pb; // 實際:pa 是指標,pb 是 char
// 有 typedef:語意清晰
typedef char* PCHAR;
PCHAR pa, pb; // 兩者皆為 char*
大量指標宣告時,typedef 可確保所有變數具備相同指標層級,減少錯誤。
// 舊式 C:宣告變數需加 struct 關鍵字
struct Point { int x, y; };
struct Point p1;
// 搭配 typedef:一次定義,省去 struct
typedef struct Point { int x, y; } Point;
Point p1; // 直接使用類型名
雖然 C++ 已允許直接用結構體標籤作類型名,但理解此用法有助維護舊代碼庫。
// 平台 A:支援 long double
typedef long double REAL;
// 平台 B:僅支援 double
typedef double REAL;
// 平台 C:僅支援 float
typedef float REAL;
標準庫廣泛採用此技巧:size_t、ptrdiff_t、int32_t 等皆為平台相關類型的別名。跨平台編譯時,僅需修改 typedef 定義處,業務代碼零修改。
採用「逐步替換法」:保留變數名到最後,由內而外替換。
// 例 1:int *(*a[5])(int, char*)
// 變數 a 是陣列,元素為函數指標,回傳 int*,參數 (int, char*)
typedef int*(*FunPtr)(int, char*);
FunPtr a[5];
// 例 2:void (*b[10])(void (*)())
// b 是陣列,元素為函數指標,參數為「無參無回傳函數指標」
typedef void (*Callback)();
typedef void (*FunPtr2)(Callback);
FunPtr2 b[10];
// 例 3:double (*(*e)[9])()
// e 是指標,指向含 9 元素的陣列,元素為「無參回傳 double 的函數指標」
typedef double (*FunPtr3)();
typedef FunPtr3 (*ArrayPtr)[9];
ArrayPtr e;
「右左法則」輔助理解:從變數名出發,先右後左,遇括號轉向,括號內分析完跳出,循環至結束。
typedef char* PSTR;
const PSTR p; // 等價於 char* const p(常數指標),而非 const char* p(指向常數的指標)
const 修飾的是整個別名類型(即指標本身),非指向的資料。解決方法:另定義 typedef const char* CPSTR;。
typedef static int INT2; // 編譯錯誤:multiple storage classes
typedef 在語法分類上屬於儲存類別(同 static、extern、register 等),一個宣告中只能出現一個儲存類別關鍵字。
| 特性 | typedef | #define |
|---|---|---|
| 處理階段 | 編譯期(語義分析) | 預處理期(文字替換) |
| 指標宣告 | typedef char* P; P a,b; 皆為 char* | #define P char* P a,b; 僅 a 為 char*,b 為 char |
| const 結合 | 語義正確(修飾類型) | 文字替換易產生語義偏差 |
| 除錯支援 | 符號表保留別名 | 預處理後消失,除錯器見不到 |
| 作用域 | 遵循 C++ 作用域規則 | 全域檔案範圍,無作用域隔離 |
using StringMap = std::unordered_map<std::string, std::string>;(C++11 using 語法更直觀)using EventHandler = std::function<void(const Event&)>;using Milliseconds = long long; using UserID = uint64_t; 而非裸露基本型別。#ifdef 切換底層類型,上層代碼完全不變。typedef 不只是「取別名」,它是類型系統的語義擴充機制。善用它能:
#define 的文字替換陷阱;現代 C++ 建議優先使用 using 別名宣告(支援模板別名、語法更一致),但理解 typedef 所有細節對維護舊代碼、深度理解類型系統仍不可或缺。