理解內存分區是掌握C語言生命週期、作用域、性能優化及調試崩潰的基石。本文詳細剖析棧區、堆區、靜態/全局區、常量區、代碼區的特性、分配機制、典型代碼示例及常見陷阱。
高地址 ┌─────────────────┐ │ 棧區 │ ← 向下增長(高→低分配,低→高使用) │ (Stack) │ ├─────────────────┤ │ 堆區 │ ← 向上增長(低→高) │ (Heap) │ ├─────────────────┤ │ 靜態/全局區 │ ← 程序啟動分配,退出釋放 │ (BSS/Data) │ ├─────────────────┤ │ 常量區 │ ← 字面量、字符串常量(只讀) │ (Literal) │ ├─────────────────┤ │ 代碼區 │ ← 二進制指令(只讀) │ (Text) │ └─────────────────┘ 低地址
// 1. 棧分配方向驗證
char a=0, b=0, c=0;
printf("%p %p %p", &a, &b, &c); // 地址遞減
// 2. 棧使用方向:低地址先用
char n1=1, n2=0;
short *p = &n2; // n2地址 > n1地址
printf("%d", *p); // 讀取n2及n1組成的short值(跨變量讀取,演示內存連續)
// 3. 函數調用棧幀
int fun1() { int n1=200; return n1; }
int fun2() { int n1; return n1; } // 未初始化!
int main() {
fun1(); // fun1棧幀銷毀,但數據殘留
int m = fun2(); // fun2復用相同棧位置,讀到殘留值200
printf("%d", m); // 輸出200(未定義行為!)
}
int a[1000000])導致棧耗盡。malloc/calloc/realloc 申請,free 釋放。free,長期運行程序致命。free(p); free(p); → 未定義行為。對策:free(p); p=NULL;。malloc/free 崩潰。static 修飾。static。
void counter() {
static int cnt = 0; // 只初始化一次
printf("%d\n", cnt++);
}
int main() {
for(int i=0;i<5;i++) counter(); // 輸出 0 1 2 3 4
}
"hello")、數值字面量(10, 3.14)。char *s="abc"; s[0]='x';)。\0 結尾,編譯器可能合併相同字符串。| 區域 | 管理方式 | 生命週期 | 典型內容 | 大小限制 | 常見問題 |
|---|---|---|---|---|---|
| 棧 | 自動 | 函數調用期間 | 局部變量、參數、返回地址 | 較小(~MB) | 溢出、懸空指針、未初始化 |
| 堆 | 手動 | malloc 到 free | 動態數據結構 | 大(~GB) | 洩漏、越界、碎片、野指針 |
| 靜態/全局 | 系統 | 程序全程 | 全局/靜態變量 | 固定 | 命名衝突、耦合、初始化順序 |
| 常量區 | 系統 | 程序全程 | 字面量 | 固定 | 誤修改導致崩潰 |
| 代碼區 | 系統 | 程序全程 | 二進制指令 | 固定 | 自我修改代碼(極少用) |
內存分區不是抽象理論,而是調試段錯誤、優化性能、防範漏洞的實戰地圖。牢記:棧上自動、堆上手動、靜態全程、常量只讀,方能遊刃有餘。