// 情況 A:正確
char* f1() { char *s = "abcdefghijkl"; return s; }
// 情況 B:錯誤(回傳區域變量地址)
char* f2() { char s[] = "abcdefghijkl"; return s; }
差異本質:字串字面量存於常量區(程式結束前釋放),區域陣列存於棧(函數返回即銷毀)。
| 區域 | 管理方式 | 存放內容 | 生命週期 |
|---|---|---|---|
| 棧 | 編譯器自動 | 函數參數、區域變量、返回地址 | 函數調用期間 |
| 堆 | 程式員手動 malloc/free 或 new/delete | 動態分配記憶體 | 手動釋放或程式結束由 OS 回收 |
| 全域/靜態區 | 編譯器靜態分配 | 全域變量、static 變量(已初始化/未初始化分段) | 程式全生命週期 |
| 常量區 | 編譯器 | 字串字面量、const 修飾的全域/靜態變量 | 程式全生命週期 |
| 代碼區 | 編譯器 | 函數二進制機器碼 | 程式全生命週期 |
int a = 0; // 全域初始化區
char *p1; // 全域未初始化區
void func() {
int b; // 棧
char s[] = "abc"; // 棧(內容複製自常量區)
char *p2; // 棧
char *p3 = "123456"; // p3在棧,"123456\0"在常量區
static int c = 0; // 全域/靜態初始化區
p1 = malloc(10); // 堆
p2 = malloc(20); // 堆
strcpy(p1, "123456"); // 字面量在常量區,可能與 p3 共享
}
| 維度 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 系統自動 | 程式員手動 |
| 空間大小 | 受限(Windows 預設 1~2MB) | 受限於虛擬記憶體,極大 |
| 生長方向 | 高位→低位 | 低位→高位(鏈表管理) |
| 分配效率 | 極快(指針移動) | 較慢(尋找合適塊、分裂合併) |
| 碎片問題 | 無 | 易產生外部碎片 |
| 適用場景 | 小型、生命週期短、大小固定 | 大型、動態、生命週期不定 |
char a[] = "1234567890"; // 棧上陣列
char *p = "1234567890"; // 指向常量區
a[1]; // 編譯為 mov cl, [ebp-0Fh] 直接偏移訪問
p[1]; // 編譯為 mov edx, [ebp-14h]; mov al, [edx+1] 間接訪問,多一條指令
結論:棧上陣列存取比指針指向的常量區/堆區更快。
static 或動態分配,避免棧溢位const char*,防止誤修改觸發段錯誤malloc/freeVirtualAlloc 直接保留位址空間