本文探討兩個經典 C 語言細節:函數返回局部數組指針導致的懸空指針,以及測試進程可分配的最大堆內存。
// ❌ 錯誤:返回局部數組指針
char* bad_favorite_fruit() {
char fruit[] = "apple"; // 棧上分配,函數返回時銷毀
return fruit; // 返回已失效內存地址 → 懸空指針
}
// ✅ 正確 1:static 延長生命周期至程序結束
char* good_favorite_fruit_static() {
static char fruit[] = "apple"; // 數據段分配,全程有效
return fruit;
}
// ✅ 正確 2:動態分配 (調用者負責 free)
char* good_favorite_fruit_malloc() {
char *fruit = malloc(6); // 堆上分配
if (fruit) strcpy(fruit, "apple");
return fruit;
}
// ✅ 正確 3:調用者提供緩衝區
void good_favorite_fruit_buf(char *buf, size_t size) {
if (size >= 6) strcpy(buf, "apple");
}
原理:局部變量 (非 static) 分配在**棧**上,函數返回時棧幀彈出,內存被標記為可復用,隨時可能被覆蓋。返回其地址即為懸空指針,解引用屬未定義行為 (UB)。
#include#include int main() { size_t total_kb = 0; // 每次申請 1KB (1024 字節) while (malloc(1 << 10) != NULL) { ++total_kb; } printf("Allocated %zu KB total\n", total_kb); // 注意:分配的內存未 free,程序退出時 OS 自動回收 return 0; }
Allocated 2048000 KB total (約 2GB,32 位進程典型值)。malloc 失敗返回 NULL,可能原因:虛擬地址空間耗盡、內存碎片化、ulimit -v 限制、過度提交策略。| 限制來源 | 查看/設置命令 | 說明 |
|---|---|---|
| 進程虛擬地址空間 | 架構決定 (32位 4GB, 64位 256TB+) | 理論上限 |
| RLIMIT_AS (虛擬內存) | ulimit -v / setrlimit | 管理員可限制進程最大虛擬內存 |
| RLIMIT_DATA (數據段/堆) | ulimit -d | 限制堆增長大小 |
| 過度提交 | /proc/sys/vm/overcommit_memory | Linux 默認允許過度提交,malloc 可能「成功」但實際用時 OOM Killer 殺進程 |