C 語言的聲明語法基於「聲明即使用」的設計哲學:聲明的形式應盡可能與使用形式相似。但這導致聲明無法簡單地從左向右閱讀,成為程序員的攔路虎。本章將系統拆解聲明規則,攻克 const、指針、結構體、聯合、枚舉、typedef 等核心難點。
()(函數)和 [](數組)—— 後綴運算符,結合性從左到右。*(指針)—— 前綴運算符,結合性從右到左。int, char 等)及 const/volatile。int *p[3]; // p 是數組 -> [3] 優先 -> * -> int:int 類型指針數組
int (*p)[3]; // p 是指針 -> * 優先 -> [3] -> int:指向 int[3] 的指針
int *foo(int); // foo 是函數 -> () 優先 -> * -> int:返回 int* 的函數
int (*foo)(int); // foo 是指釖 -> * 優先 -> (int) -> int:指向函數(int->int) 的指針
黃金法則:const 修飾它右邊緊鄰的實體(若右邊無實體,則修飾左邊)。
| 聲明 | 讀法 | 含義 |
|---|---|---|
const int *p; / int const *p; | const 修飾 *p 右邊的 int | 指向常量整數的指針(指針可變,指向的值不可變) |
int *const p; | const 修飾 p | 常量指針(指針不可變,指向的值可變) |
const int *const p; | 兩個 const | 指向常量整數的常量指針(雙重不可變) |
代碼驗證:
const int *p1; p1++; *p1 = 10; // 錯誤:不能修改 *p1
int *const p2 = &a; p2++; *p2 = 20; // 錯誤:不能修改 p2
struct Tag { ... }; 中 Tag 是標簽,非類型名。C 語言中需用 struct Tag 宣告變量;C++ 可直接用 Tag。typedef 簡化:typedef struct Student { int id; char name[20]; } Student;#define:調試器中可見符號名、作用域限制、類型檢查更強。enum class 修正此問題)。| 特性 | typedef | #define |
|---|---|---|
| 本質 | 類型別名(編譯器處理) | 文本替換(預處理器處理) |
| 指針聲明 | typedef char* PCHAR; PCHAR p1, p2; 兩個均為 char* | #define PCHAR char* PCHAR p1, p2; 展開為 char* p1, p2; p2 是 char |
| 類型擴展 | 不可擴展(unsigned PCHAR 錯誤) | 可擴展(unsigned PCHAR → unsigned char*) |
| 作用域 | 遵循塊作用域規則 | 無作用域,全局替換 |
| 調試可見性 | 符號表中保留 | 預處理後消失 |
C 有四類獨立名字空間:
goto label、struct/enum/union tag)建議:結構標簽加 _tag 後綴(如 struct Student_tag),typedef 名用同名大寫或無後綴,清晰區分。
掌握優先級規則、const 綁定原理、組合類型記憶體模型、typedef 與宏的本質差異、名字空間隔離機制,是從「會用 C」進階到「精通 C 類型系統」的必經之路。建議多練習 cdecl 工具輔助驗證複雜聲明。