K&R 承認:「C 語言聲明語法有時會帶來嚴重問題」。核心原因在於聲明形式模仿使用形式,導致無法像自然語言那樣從左到右閱讀。例如:
char * const *(*next)();
乍看之下令人頭暈目眩,但掌握系統性解析法後便能迎刃而解。
《C 專家編程》給出的標準解析步驟:
next)() → 函數,[] → 數組* → 指針,const → 常量修飾() 和 [] 同級最高,結合方向左→右;* 次之,結合方向右→左)() 將其內視為整體,跳出繼續char * const *(*next)()| 步驟 | 操作 | 當前解讀 |
|---|---|---|
| 1 | 從 next 開始,被 () 包圍 | next 是一個指向...的指針 |
| 2 | 跳出括號,向右見 () | 指向一個函數的指針 |
| 3 | 向左見 * | 該函數返回一個指針 |
| 4 | 向左見 const | 返回常量指針 |
| 5 | 向左見 char * | 指向 char 類型的常量指針 |
最終含義:next 是一個函數指針,指向一個返回指向 char 常量指針的指針的函數。
char *(* c[10])(int **p)| 步驟 | 操作 | 當前解讀 |
|---|---|---|
| 1 | 從 c 開始 | c 是... |
| 2 | 向右見 [10] | 一個大小為10的數組 |
| 3 | 向左見 * | 數組元素是指針 |
| 4 | 向右見 (),參數 int **p | 指向函數的指針,參數為指向指針的指針 |
| 5 | 向左見 char * | 函數返回指向 char 的指針 |
最終含義:c 是一個10元素數組,每個元素是函數指針,指向接受 int** 參數、返回 char* 的函數。
優先級 運算符 結合方向 說明 1 () [] 左→右 函數調用、數組下標 2 * & + - ~ 右→左 一元運算符(指針、取址、正負、按位非) 3 * / % 左→右 乘除模 4 + - 左→右 加減 5 << >> 左→右 移位 6 < <= > >= 左→右 關係 7 == != 左→右 相等 8 & 左→右 按位與 9 ^ 左→右 按位異或 10 | 左→右 按位或 11 && 左→右 邏輯與 12 || 左→右 邏輯或 13 ?: 右→左 條件 14 = += -= ... 右→左 賦值 15 , 左→右 逗號
聲明中實際參與的優先級(由高到低):
() 函數參數列表、[] 數組下標* 指針修飾符const, volatile 等)int, char, 結構體等)cdecl 工具與「右左法則」口訣
// Linux 系統自帶神器
$ cdecl explain "char *(*c[10])(int **p)"
declare c as array 10 of pointer to function (pointer to pointer to int) returning pointer to char
口訣版「右左法則」:
「從變量名出發,先向右後向左,括號內優先,遇括號整體看;
右有括號是函數,右有方括號是數組;
左有星號是指針,左有類型是基礎;
const 修飾左邊物,若在星號右修飾指向值。」
| 聲明 | 含義 |
|---|---|
int *p[10] | 指針數組:10個 int* 元素 |
int (*p)[10] | 數組指針:指向 10 個 int 的數組 |
int *f() | 函數:返回 int* |
int (*f)() | 函數指針:指向返回 int 的函數 |
void (*signal(int, void(*)(int)))(int) | 經典 signal 函數原型 |
typedef int (*FuncPtr)(int**);
typedef FuncPtr (*FuncPtrArray)[10];
FuncPtrArray c; // 瞬間清晰
cdecl / 在線解析工具 驗證理解掌握 C 聲明解析不僅是面試必備,更是閱讀系統級代碼(Linux 內核、驅動程序、嵌入式 SDK)的生存技能。建議將優先級表打印放在工位,養成「機械化解析」的肌肉記憶。