// 情況一:返回字符串字面量指標
char* toStr1() {
char *s = "abcdefghijkl"; // s 在棧,字符串在常量區
return s; // 返回常量區地址,安全
}
// 情況二:返回局部數組指標
char* toStr2() {
char s[] = "abcdefghijkl"; // 整個數組在棧
return s; // 返回棧地址,函數結束棧幀銷毀 → 野指標!
}
第一個打印正常,第二個印亂碼。核心差異在於字符串字面量存放在常量區(程式結束才釋放),而局部數組存放在棧區(函數返回即釋放)。
| 區域 | 別名 | 存放內容 | 管理方式 | 生命週期 |
|---|---|---|---|---|
| 棧區 | Stack | 函數參數、局部變數、返回地址、暫存器保存 | 編譯器自動分配/釋放 | 函數調用期間 |
| 堆區 | Heap | 動態分配內存 | 程式員手動 malloc/free/new/delete | 手動控制,程式結束 OS 回收 |
| 全局/靜態區 | Data/BSS | 全局變數、static 變數 | 編譯器分配,程式結束釋放 | 整個程式運行期間 |
| 常量區 | Literal Pool / .rodata | 字符串字面量、const 修飾的全局/靜態變數 | 編譯器分配,通常唯讀 | 整個程式運行期間 |
| 代碼區 | Text / .text | 函數二進制機器碼 | 唯讀,編譯期確定 | 整個程式運行期間 |
int a = 0; // 全局初始化區
char *p1; // 全局未初始化區
int main() {
int b; // 棧
char s[] = "abc"; // 棧 (數組內容複製自常量區)
char *p2; // 棧
char *p3 = "123456"; // p2在棧,"123456"在常量區
static int c = 0; // 全局/靜態初始化區
p1 = malloc(10); // 堆區 10 bytes
p2 = malloc(20); // 堆區 20 bytes
strcpy(p1, "123456"); // 複製到堆,"123456"在常量區
}
| 維度 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 系統自動 | 程式員手動 |
| 效率 | 極快 (指標遞增/遞減) | 較慢 (鏈表遍歷、碎片整理) |
| 大小限制 | 小 (Windows 預設 1-2MB) | 大 (受限於虛擬記憶體) |
| 生長方向 | 向低地址 | 向高地址 |
| 碎片 | 無 | 有 |
| 適用場景 | 小型、生命週期固定的局部變數 | 大型、動態、生命週期不確定的數據 |
char buf[1024*1024]。malloc 遍歷鏈表找首個滿足大小的塊 (First-fit)。free 使用。
char s1[] = "aaaaaaaaaa"; // 棧上數組
char *s2 = "bbbbbbbbbb"; // 指向常量區
匯編對比 (x86):
; a = s1[1] -- 直接基址偏移
mov cl, byte ptr [ebp-0Fh]
mov byte ptr [ebp-4], cl
; a = s2[1] -- 先取指標值,再間接寻址
mov edx, dword ptr [ebp-14h]
mov al, byte ptr [edx+1]
mov byte ptr [ebp-4], al
棧上數組少一次間接寻址,更快。但現代 CPU 快取機制下差異極微,正確性優先。
free(p); p = NULL; 習慣置空。double free 崩潰。malloc 對應 free,工具檢查。char data[],單次 malloc 分配整塊,提升緩存局部性。Java、Go、Python、Rust 等語言雖有 GC,但底層同樣有棧 (Stack) 與堆 概念:
理解 C/C++ 手動內存管理,能幫你在任何語言中寫出更高性能、更少 Bug 的代碼。