C 宣告語法以「優先序規則」聞名遐邇,搭配 typedef 能大幅提升可讀性。本文系統整理解析步驟、名稱空間隔離、typedef vs 宏的本質差異,並給出實戰範本。
標準解析步驟:
()(函式)、[](陣列) > 前綴 *(指標) > 型別修飾詞(const/volatile)。const/volatile 緊跟型別修飾型別,否則修飾左側最近的 *。char * const *(*next)();
next 是 ...(*next) → 指標(*next)() → 指向函式的指標*(*next)() → 函式回傳指標const *(*next)() → 回傳 const 指標char * const *(*next)() → 回傳「指向 char 的 const 指標」的指標結論:next 是「指向函式的指標,該函式回傳『指向 char 的常數指標』的指標」。
| 特性 | #define | typedef |
|---|---|---|
| 處理階段 | 前處理(文字替換) | 編譯(語意分析) |
| 可加修飾詞 | unsigned INT_PTR 合法 | unsigned MyIntPtr 錯誤 |
| 多變數宣告 | INT_PTR a, b; → int *a, b; (b 非指標) | MyIntPtr a, b; 皆為指標 |
| 遞迴定義 | 可能無限展開 | 編譯器偵測循環 |
signal 原型簡化/* 原始原型:難以閱讀 */ void (*signal(int sig, void (*func)(int)))(int); /* typedef 兩步走 */ typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
goto label;struct/union/enum 標記(struct Foo)s.member)typedef 名稱、列舉常數不同名稱空間可重名:
struct Foo { int Foo; }; /* 合法:標記 vs 成員 */
typedef struct Foo Foo; /* 合法:標記 vs typedef */
Foo Foo; /* 合法:typedef 名 vs 變數名 */
typedef struct Fruit { int weight; } Fruit; /* 同時引入標記 Fruit 與型別 Fruit */
struct Fruit apple; /* 以標記宣告 */
Fruit banana; /* 以型別宣告 */
struct Veg { int weight; } Veg; /* 標記 Veg + 變數 Veg(非型別!) */
struct Veg carrot; /* 僅能用標記 */
Veg potato; /* 錯誤:Veg 非型別名 */
類比:int i; i j; 同樣非法。
enum Color { RED = 1, GREEN, BLUE = 5, YELLOW }; /* RED=1, GREEN=2, BLUE=5, YELLOW=6 */
enum { MAX_SIZE = 1024 }; /* 匿名列舉,常數可直接用 MAX_SIZE */
int 常數運算式,可用於 case、位欄寬度、陣列大小。#define。enum : uint8_t { ... } 指定底層型別。const int *p; /* p 可變,*p 唯讀 */ int * const p; /* p 唯讀,*p 可變 */ const int * const p;/* 皆唯讀 */ /* 函式參數 */ void foo(const int *p); /* 承諾不修改 *p */ void bar(int * const p); /* 承諾不修改 p 本身(罕用) */
const 物件若未被 volatile 修飾且位址未逃逸,編譯器常直接以立即數替換,不配置儲存空間。const 非「編譯期常數」(C 不支援 const int n; int a[n];),需用 enum 或宏。宣告即文件,typedef 即抽象。善用「右左法則」解讀複雜宣告,用 typedef 封裝函式指標與平台相關型別,能讓 C 程式碼既精確又易維護。