本文整理自《C 專家編程》前兩章的核心內容,涵蓋 ANSI C 標準、const 關鍵字深度解析、整型提升與算術轉換、常見陷阱等專家級知識。適合進階 C 語言學習者查漏補缺。
編譯器設計者遵循「效率(幾乎)就是一切」的原則。這意味著標準留給實現的自由度很大,程序員不應依賴未定義行為或實現定義行為。
建議直接閱讀 ANSI C 標準文檔。重點關注:
`const` 的位置決定其修飾對象,口訣:「右邊最近的類型才是被保護的」。
| 聲明 | 含義 | 可修改指針值 | 可修改指向內容 |
|---|---|---|---|
| `const int *p` / `int const *p` | 指向常量的指針 | 是 | 否 |
| `int * const p` | 常指針(指針本身是常量) | 否 | 是 |
| `const int * const p` | 指向常量的常指針 | 否 | 否 |
int main(void) {
const int x = 2, y = 3; // 兩個常量
int z = 3; // 普通變量
const int *pc = &x; // 指向常量的指針
int * const cp = &z; // 常指針
int *pp = &z; // 普通指針
pc = &z; // ✓ pc 可指向其他變量
// *pc = 10; // ✗ 不能通過 pc 修改值
pc = pp; // ✓ 普通指針賦值給指向常量的指針
// pp = pc; // ✗ 指向常量的指針不能賦值給普通指針(會丟失 const 屬性)
*cp = x; // ✓ 通過常指針修改指向變量的值
// cp = &x; // ✗ 常指針值不可修改
// pp = &x; // ✗ 不同編譯器行為不同,標準禁止
// pp = pc; // ✗ 同上
pp = cp; // ✓ 常指針可賦值給普通指針
const int * const cpc = &x; // 雙重 const
return 0;
}

關鍵規則:
char *str[] = {
"aaa", "bbb", "ccc", "ddd"
"eee", // 此處漏逗號,"ddd" 和 "eee" 合併為 "dddeee"
};
// 數組長度變為 4 而非 5
函數內 `static` 變量僅初始化一次,可用於實現「首次調用與後續調用行為不同」的邏輯:
void func(void) {
static int first_call = 1;
if (first_call) {
// 首次執行邏輯
first_call = 0;
} else {
// 後續執行邏輯
}
}
`i = 1, 2;` 等價於 `(i = 1), 2`,結果 `i = 1`。
`i = (1, 2);` 結果 `i = 2`(逗號表達式取最後一個值)。

C 標準未規定函數參數求值順序(多數為右向左),且同一序列點多次修改同一對象屬於未定義行為:
i = 3;
printf("%d, %d\n", i, i++); // 輸出 4, 3(右向左求值)
i = 3;
j = (++i) + (++i) + (++i); // j = 16, i = 6(GCC,但標準未定義)
int i = 1, j = 1, k;
i = (i++) + (++i); // i = 5(GCC)
k = (j++) + (++j); // k = 4, j = 3
警告:上述多重副作用代碼屬於未定義行為,不同編譯器結果不同,實際工程中嚴禁編寫。