C 語言的指針聲明靈活強大,但嵌套層次一多,讀起來就像天書。例如 int (*(*func)(int *))[5]; 這樣的聲明,乍看之下極難辨別 func 究竟是什麼類型。ANSI C 標準定義了聲明的語法規則(如何寫),而「右左法則」是從規則中歸納出的「如何讀」方法論。掌握它,能讓你在面試、維護舊碼、編寫系統級程式時游刃有餘。
從標識符出發,先右後左,遇括號反向,跳出繼續,直到結束。
具體步驟:
[] →「陣列」;遇到 () →「函數」;遇到 )(右括號)→ 停止向右,轉向左。* →「指針」;遇到類型關鍵字(int、void 等)→「基礎類型」;遇到 ((左括號)→ 停止向左,跳出括號,方向反轉(再向右)。int* (*a[5])(int, char*);a。[5] → 含 5 個元素的陣列。* → 指針(指向下一層)。(int, char*) → 函數,參數為 int 和 char*。int* → 返回值為 int*。a 是一個陣列,元素是指針,指向「參數為 int, char*、返回 int* 的函數」。void (*b[10]) (void (*)());b → 陣列[10] → 指針 → 函數(參數:void (*)() 函數指針) → 返回 void。int(*)() (*c)[9];c → 指針 → 陣列[9] → 元素為 int(*)()(指向無參、返回 int 函數的指針)。int (*(*d)[5])(int *);分層拆解:
(*d) → d 是指針
(*d)[5] → 指向含 5 元素陣列的指針
*(*d)[5] → 陣列元素是指針
int (int *) → 指向「參數 int*、返回 int」函數的指針
結論:d 是指針,指向一個陣列,陣列元素是函數指針(參數 int*,返回 int)。
int (*(*e)(int *))[5];(最經典的「函數返回陣列指針」)e → 向右遇 ),向左遇 * → e 是指針。(int *) → 函數,參數 int*。* → 返回指針。[5] → 指向含 5 元素陣列的指針。int → 基礎類型 int。e 是函數指針,指向「參數 int*、返回 int (*)[5](指向 int[5] 的指針)的函數」。typedef 拆解複雜聲明實際工程中,嚴禁 直接寫出上述單行聲明,維護性極差。應逐層 typedef:
// 原聲明:int (*(*func)(int *))[5];
typedef int (*ArrPtr)[5]; // 1. 定義「指向 int[5] 的指針」
typedef ArrPtr (*FuncPtr)(int*); // 2. 定義「參數 int*、返回 ArrPtr 的函數指針」
FuncPtr func; // 3. 聲明變數,清晰易讀
(*(void (*)())0)()這是一個「將地址 0 強轉為函數指針並調用」的技巧(常見於嵌入式啟動代碼):
(void (*)()) → 類型:返回 void、無參的函數指針。(void (*)())0 → 將 0 強轉為該類型指針(指向地址 0)。*(void (*)())0 → 解引用,得到函數名稱(地址 0 處的函數)。(*(void (*)())0)() → 調用該函數。typedef 化簡:
typedef void (*pf)();
(*(pf)0)(); // 同義,可讀性大增
typedef 分層,降低認知負荷。