前言
本文匯總 C 語言中級階段的核心知識點,涵蓋 sizeof 語義、結構體對齊規則、指針/陣列/函數指針複雜聲明、記憶體分區、堆使用四要素、預處理宏陷阱、頭檔管理、函數參數求值順序等,適合進階鞏固與面試速查。
一、sizeof 核心規則
- **編譯期計算**,非函數,無運行開銷。
sizeof(型別) 需括號;sizeof 變數 括號可選,建議統一加。
sizeof(指針) = 指針大小(32位=4,64位=8),**不等於** 指向物件大小。
int a[5]; sizeof(a)=20(陣列大小);sizeof(&a)=4/8(指向陣列的指針大小)。
&a + 1 跳過整個陣列(型別 int(*)[5]),a + 1 跳過一個元素(型別 int*)。
二、結構體對齊三大鐵律
- **成員偏移**:每個成員偏移必須是 **min(成員大小, 對齊基數)** 的倍數(基數通常為編譯器選項
#pragma pack(n) 或預設 4/8)。
- **總大小對齊**:結構體總大小為 **max(最寬基本型別大小, 對齊基數)** 的整數倍,不足尾部填充。
- **巢狀結構體視為整體**:巢狀結構體作為成員時,其偏移按**自身對齊要求**對齊,大小計入外層總大小。
struct A { int i; double d; }; // sizeof=16 (i@0, d@8)
struct B { int i; struct A a; double d; }; // sizeof=32 (a@8 對齊8, d@24)
三、字串字面常數與轉義
- 相鄰字串字面常數自動連接:
"hello" "world" → "helloworld"。
- 八進制轉義:
\323 = 0xD3;\32"3 → \32 + '3'。
- 十六進制轉義:
\x60 = 0x60。
四、陣列下標與指針等價性
a[i] == *(a+i) == *(i+a) == i[a](交換律)。
- 陣列名在**表達式**中退化為指向首元素的指針(
int *const 屬性,不可修改指向)。
- 二維陣列
int b[3][4]:b 型別 int(*)[4],b+1 跳過一行(16字節)。
*(*(b+2)+0) = b[2][0];*(b+2) = 第3行首地址;b+2 = &b[2][0]。
五、有符號/無符號運算陷阱
- 混合運算時,**有符號隱式轉換為無符號**(整數提升規則)。
-1 > 0u 為真(-1 轉為巨大無符號數)。
- 比較/列印時務必統一符號性,或顯式轉型。
六、記憶體五大分區(C++)/ 四大分區(C)
| 區域 | C/C++ 共同 | C++ 額外 | 典型內容 |
| 棧 | ✓ | | 區域變數、函數參數、返回地址、幀指標 |
| 堆 | ✓ (malloc/free) | new/delete | 動態分配記憶體 |
| 全域/靜態 | ✓ | | 全域變數、static 變數、字串字面常數 |
| 常量區 | ✓ | | 字串字面常數、const 全域(可能合併只讀段) |
| 自由存儲區 | | new/delete | C++ 對象動態分配(與堆常重疊) |
七、堆記憶體使用四要素(鐵律)
int *p = (int*)malloc(10 * sizeof(int));
if (p == NULL) return -1; // 1. 必須檢查分配失敗
// ... 使用 p ...
free(p); // 2. 必須釋放
p = NULL; // 3. 必須置空防野指針
// 4. 不要重複 free,不要 free 非堆指針
八、指針進階:複雜聲明讀寫指南
typedef float (*PF)(int, double); // 函數指針型別
int (*p)[3]; // 指向 int[3] 的指針(二維陣列行指針)
int *ptr[3][4]; // 二維指針陣列
int *(*ptr)[4]; // 指向「4元素指針陣列」的指針
int (*myfun())(int*, int*); // 返回函數指針的函數
int (*s[10])(int); // 10個函數指針的陣列
**右左法則**:從變數名出發,先右後左,遇括號反向,循環至解析完。
九、函數指針賦值與調用
void myfun(int x) { printf("%d", x); }
void (*pfun)(int) = myfun; // 賦值:函數名即入口地址
pfun(10); // 調用方式1
(*pfun)(10); // 調用方式2(等價)
十、預處理宏陷阱與現代替代
- 宏參數**副作用風險**:
#define SQR(x) ((x)*(x)) → SQR(i++) 導致 i 增加兩次。
- 宏無型別檢查、無作用域、除錯不可見。
- **現代替代**:無參宏 →
const / enum;有參宏 → inline 函數 / constexpr (C++)。
# 字串化:#define STR(x) #x → STR(abc) → "abc"。
## 連接:#define CAT(a,b) a##b → CAT(var,1) → var1。
十一、頭檔管理鐵律
- **護衛宏**:
#ifndef FILE_H #define FILE_H ... #endif。
- 頭檔**只聲明,不定義**(
extern 變數、inline 函數、模板除外)。
- **
< > vs ""**:系統頭檔用尖括號(搜尋系統路徑);專案頭檔用雙引號(先搜尋當前目錄)。
- 頭檔**不放大型
static 變數**(每個編譯單元產生一份拷貝,浪費空間且易誤用)。
十二、函數參數求值順序未定義
- C 標準**不規定**參數求值順序(左→右、右→左、交錯皆合法)。
func(i++, i++) → **未定義行為**。
- 鐵律:參數表達式**無副作用**,複雜計算先存入臨時變數再傳遞。
十三、總結
這 13 大知識點構成了 C 語言從「能跑」到「精通」的分水嶺。反覆閱讀、動手驗證(特別是結構體對齊、指針複雜聲明、宏副作用),將它們內化為直覺,是邁向系統級、嵌入式、高效能 C 開發者的必經之路。
- 上一篇文章: 深度解析 extern "C":C++ 與 C 混合編程的連結橋樑
- 下一篇文章: C語言面試題精選詳解(一):static、堆棧、switch、float比較、轉換函數、測試分類、設計階段、指針運算、宏陷阱
Copyright ©
程式碼之家 All Rights Reserved