編譯器設計者的金科玉律:在正確性前提下極致追求效率。這解釋了許多看似怪異的優化行為(如刪除無副作用代碼、重排指令)。
標準文檔(如 C89/C99)是解決語言律師級爭議的最終權威。重點關注:
char* 可作為參數傳遞,但 char** 不能隱式轉換為 const char**(防止通過雙重指針修改常量)。void f(int a[][10]),因為編譯器需計算行偏移。const 是 C 語言最易誤用的關鍵字之一,核心在於區分指針本身與指向的數據。
const int *p; // (1) 指向常量的指針:數據只讀,指針可變
int *const p; // (2) 常指針:指針只讀,數據可讀寫
const int *const p; // (3) 雙重 const:指針與數據均只讀
int *p; // (4) 普通指針:雙方均可讀寫
從變量名向左讀:
const int *p→ p 是 pointer to int const → 指向常量的指針
int const *p→ 同上(等價)
int *const p→ p 是 const pointer to int → 常指針
| 源類型 \ 目標類型 | int* | const int* | int* const | const int* const |
|---|---|---|---|---|
| int* | ✅ | ✅ | ❌ | ❌ |
| const int* | ❌ | ✅ | ❌ | ✅ |
| int* const | ✅ | ✅ | ✅ | ✅ |
| const int* const | ❌ | ✅ | ❌ | ✅ |
核心原則:只能增加 const 資格,不能減少。將「指向常量的指針」賦值給「普通指針」會打破只讀承諾,故非法。
int main() {
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; // ✅ 普通指針 → 指向常量指針(增加 const)
// pp = pc; // ❌ 指向常量指針 → 普通指針(減少 const)
*cp = x; // ✅ 通過常指針修改數據合法
// cp = &x; // ❌ 常指針不可修改指向
// pp = &x; // ⚠️ 實現定義(某些編譯器警告)
pp = cp; // ✅ 常指針 → 普通指針(指針值拷貝,不涉及 const 轉換)
const int *const cpc = &x; // 雙重 const:終極保護
return 0;
}
C 語言整型運算遵循嚴格的類型提升規則,違反直覺是大量 Bug 溫床。
char、short、bool、位域等「窄類型」參與運算前自動提升為 int(或 unsigned int)。long double > double > float > unsigned long long > long long > unsigned long > long > unsigned int > int。
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
// sizeof 結果為 size_t (無符號)
// 與有符號 int 比較時,int 被轉為無符號
// 若 array 為空,結果為 0,但負數索引轉為巨大無符號數 → 死循環/越界
// 修正:強制轉換為有符號
#define TOTAL_ELEMENTS (int)(sizeof(array) / sizeof(array[0]))
// ❌ 錯誤:漏掉結尾 \0
char *p = malloc(strlen(str));
// ✅ 正確:+1 容納 \0
char *p = malloc(strlen(str) + 1);
char *str[] = {
"aaa", "bbb", "ccc", "ddd"
"eee" // ← 漏掉逗號!編譯器不報錯,自動拼接為 "dddeee"
};
// 結果:數組長度 4 而非 5,邏輯錯誤難排查
97% 的無break是錯誤。且default拼寫錯誤編譯器不檢查(視為普通標籤)。
i = 1, 2; // 等價於 (i = 1), 2 → i = 1
i = (1, 2); // 逗號表達式取右值 → i = 2
賦值運算符優先級高於逗號運算符。
void init_once() {
static int initialized = 0;
if (!initialized) {
// 首次執行初始化邏輯
initialized = 1;
}
// 後續執行邏輯
}
const 讀聲音法,賦值只能加 const 不能減。sizeof 結果無符號,比較時強轉。-Wmissing-braces 等警告。switch 每個 case 必加 break 或註釋 /* falls through */。