兩者均可創建類型別名,但本質截然不同:#define 是**預處理器文本替換**,typedef 是**編譯器類型系統別名**。
#define INT_D int* // 文本替換 typedef int* INT_T; // 類型別名 INT_T a, b; // 等價於 int *a, *b; 兩個指針 ✅ INT_D c, d; // 替換後 int* c, d; → c 是指針,d 是 int ❌ 陷阱! // 類型擴展 unsigned INT_D e; // 替換後 unsigned int* e; 合法 (指針類型不可加 unsigned,但這裡語法通過,語義怪異) // unsigned INT_T f; // 錯誤:typedef 類型不能參與類型擴展組合
| 特性 | #define | typedef |
|---|---|---|
| 處理階段 | 預處理 (文本替換) | 編譯 (語義分析) |
| 作用域 | 文件級 (無作用域,定義後全局生效) | 遵循 C 作用域規則 (塊/文件) |
| 多變量聲明 | 危險:INT_D a, b 非等價 | 安全:INT_T a, b 同類型 |
| 類型組合 | 允許 unsigned INT_D (文本拼接) | 禁止:unsigned INT_T 報錯 |
| 指針/數組/函數類型 | 極易出錯 | 清晰可讀:typedef int (*FP)(int); |
| 調試符號 | 無 (替換後消失) | 保留 (調試器可識別別名) |
| 可移植類型定義 | 不推薦 | 標準做法:typedef int32_t my_int; |
// ❌ 容易混淆:結構體標籤、類型別名、變量名同名
struct foo { int foo; } foo; // 合法但極差:foo 同時是標籤、變量名、成員名
// foo test; // 錯誤:struct foo 才是類型名
// ✅ 推薦 1:分離標籤與 typedef (C 風格)
struct Foo { int value; };
typedef struct Foo Foo; // 或 typedef struct Foo { int value; } Foo;
// ✅ 推薦 2:匿名結構體直接 typedef (現代 C 風格)
typedef struct { int value; } Foo;
// ✅ 使用建議:保留 struct 關鍵字提示類型性質
struct Node { int data; struct Node *next; }; // 遞歸指針必須用 struct 標籤
typedef 定義類型別名(指針、結構體、函數指針、平台相關類型)。語義清晰、作用域安全、調試友好。#define 僅用於常量字面值 (如 #define MAX 100) 或編譯條件控制 (如 #define DEBUG)。struct 關鍵字而濫用 typedef 導致標籤/變量名混淆。保留 struct Tag 形式可提升代碼可讀性 (明確標識為用戶定義類型)。typedef int32_t int32; 遷移平台時只需修改此處。