一、sizeof 關鍵細節
- **編譯期常數** (VLA 除外),不產生執行期指令。
- `sizeof(型別)` 需括號,`sizeof 變數` 可無括號。
- `sizeof(指標)` = 4 (32-bit) / 8 (64-bit),**不等於** 所指物件大小。
- 陣列名在 `sizeof` 下**不退化**為指標:`int a[10]; sizeof(a) == 40`。
- `&a` 型別為 `int (*)[10]`,`sizeof(&a) == 4/8` (指標大小)。
二、結構體對齊三大規則
- **成員偏移**:能被 `min(成員大小, 對齊基數)` 整除。
- **總大小**:為 `max(最大成員大小, 對齊基數)` 的倍數,不足尾端填充。
- **巢狀結構體**:視為整體,按其自身對齊需求對齊。
struct A { int i; double d; }; // 4 + 4pad + 8 = 16
struct B { int i; struct A a; double d; }; // 4 + 4pad + 16 + 8 = 32
三、字串字面值與指標
char *p = "hello"; // 錯誤:字串字面值唯讀,應為 const char *
const char *p = "hello"; // 正確
char arr[] = "hello"; // 陣列在堆疊,可修改
- 字串字面值儲存於 **唯讀區** (`.rodata`),修改導致段錯誤。
- 相同字面值可能合併 (編譯器最佳化),`"abc" == "abc"` 可能為真。
四、指標進階分類
| 宣告 | 含義 | 可修改項 |
| `const int *p` | 指向常數整數 | p ✅, *p ❌ |
| `int *const p` | 常數指標 | p ❌, *p ✅ |
| `const int *const p` | 常數指標指向常數 | p ❌, *p ❌ |
| `void *p` | 無型別指標 | 不可算術運算,僅賦值/比較/轉型 |
五、typedef 陷阱
typedef char *pstr;
const pstr s; // 展開為 char *const s (常數指標),**非** const char *s
**教訓**:`typedef` 非文字替換,`const` 修飾整個別名實體。
六、函數指標完整語法
/* 回傳 int,參數 (int, double) */
typedef int (*FuncPtr)(int, double);
/* 回傳函數指標的函數 */
int (*get_handler(int cmd))(int, double);
/* 函數指標陣列 */
int (*handlers[10])(int);
/* 指向函數指標陣列的指標 */
int (*(*ph)[10])(int);
**右左法則**:從變數名出發,先右後左,遇括號優先處理。
七、函數參數與呼叫慣例
- **參數求值順序未定義** (C 標準),嚴禁 `func(i++, i++)` 或 `func(f(), g())` 有副作用。
- **後置遞增** 在參數中:副作用完成時機**未指定**,不同編譯器結果不同。
- **預設 `extern`** 連結,檔案內部函數加 `static` 限制連結範圍。
- **遞迴/靜態區域變數** 破壞函數純粹性,慎用。
八、前置處理器實戰模式
| 模式 | 範例 |
| 編譯期斷言 | `_Static_assert(sizeof(int)==4, "int_must_be_32bit");` (C11) |
| 字串化 | `#define STR(x) #x` → `STR(hello)` → `"hello"` |
| 標記連接 | `#define CAT(a,b) a##b` → `CAT(var,1)` → `var1` |
| 變參宏 | `#define LOG(...) fprintf(stderr, __VA_ARGS__)` |
| 防重複包含 | `#pragma once` 或 `#ifndef GUARD_H ... #endif` |
| 條件編譯 | `#ifdef DEBUG ... #endif`、`#if VERSION > 2` |
九、頭文件設計原則
- **僅含宣告**:型別定義、函數原型、`extern` 變數宣告、宏/內聯函數。
- **禁用定義**:非 `static`/`inline` 函數定義、非 `const` 變數定義 → 連結錯誤。
- **自包含測試**:頭文件單獨 `#include` 能編譯通過。
- **最小依賴**:前向宣告代替包含其他頭文件。
十、常見未定義行為 (UB) 清單
- 有號整數溢位
- 解參考空指標/野指標
- 陣列越界存取
- 修改字串字面值
- 無序列點雙重修改同一變數 (`i = i++`)
- 讀取未初始化區域變數
- 無效指標運算 (指向不同物件的指標相減)
結語
C 語言中級門檻在於 **精準掌握型別系統、記憶體模型、指標代數、編譯期/執行期界限、UB 邊界**。將上述十大類知識點內化為肌肉記憶,配合 `-Wall -Wextra -Werror -fsanitize=address,undefined` 日常編譯,即可寫出健壯、高效、可移植的系統級代碼。
- 上一篇文章: C 語言面試題精選詳解 (一):關鍵字、記憶體、指標、演算法與系統基礎
- 下一篇文章: 深入理解 extern "C":C/C++ 混合編程、動態庫導出、跨語言呼叫的關鍵
Copyright ©
程式碼之家 All Rights Reserved