運算符優先級與關鍵行為
| 運算符 | 說明 | 陷阱 |
| ++a / a++ | 前置/後置自增 | a++ 值為舊值,副作用在序列點後 |
| & / | / ^ | 位與/或/異或 | 非邏輯運算,位運算優先級低於 == |
| ~ | 按位取反 | ~1 = ...1110 (補碼) |
| ? : | 條件運算符 | 右結合,a?b:c?d:e 解析為 a?b:(c?d:e) |
| , | 逗號運算符 | 左到右求值,值為最後表達式值 |
控制流關鍵點
switch:表達式為整型,case 後跟常量,**貫穿** 需 break
continue:結束本次循環,跳到判斷條件
scanf:標量參數**必須加 &**,數組名不需
- 邏輯運算:
&& 短路,& 不短路
- 判斷用
== 非 =,習慣寫 5 == x 防誤賦值
常用算法模板
最大公約數 (輾轉相除法)
int gcd(int a, int b) {
while (b) { int t = a % b; a = b; b = t; }
return a;
}
int lcm(int a, int b) { return a / gcd(a,b) * b; } // 先除後乘防溢出
隨機數生成
// [x, y] 範圍
int rand_range(int x, int y) { return x + rand() % (y - x + 1); }
// 例:10~100 → 10 + rand() % 91
水仙花數判斷
// 153 = 1³ + 5³ + 3³
bool is_narcissistic(int n) {
int sum = 0, t = n;
while (t) { int d = t%10; sum += d*d*d; t/=10; }
return sum == n;
}
字符串長度未知遍歷
for (int i = 0; str[i]; i++) { ... } // 利用 \0 結束
// 或
char *p = str;
while (*p) { ...; p++; }
冒泡排序 (教學用)
for (int i = 0; i < n-1; i++)
for (int j = 0; j < n-1-i; j++)
if (a[j] > a[j+1]) { swap(a[j], a[j+1]); }
字符數組初始化差異
| 寫法 | 內存佈局 | 可修改性 |
char *s = "ok"; | 代碼區字面量 + 棧上指針 | ❌ 指向只讀區 |
char s[] = "ok"; | 棧上數組 {'o','k','\0'} | ✅ 可修改內容 |
char s[] = {'o','k'}; | 棧上數組 {'o','k'} **無 \0** | ✅ 但非 C 字符串 |
安全輸入模板
// 讀整行含空格
char line[1024];
fgets(line, sizeof(line), stdin);
line[strcspn(line, "\n")] = '\0'; // 去換行
// 讀單詞 (非空白序列)
char word[100];
scanf("%99s", word); // 限寬防溢出
內存管理鐵律
- 誰分配,誰釋放:malloc/free、new/delete 成對
- 指針 free 後置 NULL:防野指針二次釋放
- 結構體內指針成員:釋放結構體前先釋放成員指向內存
- 檢查分配返回值:
if (!p) handle_error();
常見未定義行為 (UB) 紅線
1. 有符號整數溢出:INT_MAX + 1 → UB (無符號定義為模 2ⁿ)
2. 空指針解引用:int *p = NULL; *p = 1; → Crash
3. 數組越界訪問:int a[10]; a[10] = 0; → UB
4. 使用釋放後內存:free(p); *p = 1; → UB
5. 無序列點修改同一變量兩次:a = a++; → UB
調試技巧速查
printf 大法:關鍵點打印變量值、指針地址
- Assert 斷言:
assert(ptr != NULL); Debug 捕獲邏輯錯誤
- GDB/LLDB:斷點、單步、查看內存、回溯棧
- Valgrind/ASan:檢測內存洩漏、越界、使用後釋放
- 靜態分析:Clang Static Analyzer、Cppcheck、Coverity
總結
C 語言「簡單但不易」。掌握運算符優先級、內存模型、未定義行為紅線、標準庫安全用法、調試工具鏈,比記憶晦澀語法細節更實用。養成「防禦性編程」習慣:輸入驗證、邊界檢查、錯誤處理、資源管理,是寫出健壯 C 代碼的核心素養。