搞懂 C 程序內存五大區域的特性、生命週期、分配方式,是寫出無內存錯誤代碼的前提。本文結合代碼驗證,系統梳理每一區的「性格」與「避坑指南」。
| 區域 | 存放內容 | 生命週期 | 管理方式 | 典型例子 |
|---|---|---|---|---|
| 棧 | 局部變量、函數參數、返回地址 | 作用域結束自動銷毀 | 編譯器自動管理(先進後出) | int a; char buf[10]; |
| 堆 | 動態分配內存 | 手動申請/釋放 | 程序員管理(malloc/free) | int *p = malloc(100); |
| 靜態區 | 全局變量、static 變量 | 程序啟動到結束 | 編譯器分配,默認 0 初始化 | static int cnt; global_var; |
| 常量區 | 字符串字面值、const 修飾的全局變量 | 程序全周期 | 只讀,**不可修改** | "hello", 'c', 3.14 |
| 代碼區 | 二進制機器指令(函數體) | 程序全周期 | 只讀,可共享 | void func() { ... } |
int a = 10;
const int m = 10;
int *b = malloc(sizeof(int) * 2);
printf("&m=%p, &a=%p, b=%p\n", &m, &a, b);
// 典型輸出:棧地址高,堆地址低(或相反,視架構),但棵增長方向固定
void test3() {
static int a = 10; // 僅首次調用執行賦值
a++;
printf("%d\n", a); // 輸出 11, 12, 13...
}
test3(); test3(); test3();
char *str = "iphone"; // str 指向常量區,不可寫 char str1[] = "iphone"; // str1 在棧,內容是常量區拷貝,可寫 str1[0] = 'I'; // ✅ 合法 str[0] = 'I'; // ❌ 運行期崩潰(段錯誤) // 防禦:const char *str = "..."; // 編譯期報錯,更早發現
| 函數 | 特性 | 適用場景 |
|---|---|---|
malloc | 不初始化,殘留舊數據 | 大塊內存、後續會覆寫 |
calloc | 自動歸零,較慢 | 需要零初始化的結構體/數組 |
realloc | 擴容/縮容,可能移動內存 | 動態增長的緩衝區 |
memset | 按字節填充 | 字符數組初始化、結構體歸零 |
memcpy | 內存拷貝,不關心類型 | 任意類型數據移動 |
memcmp | 內存比較 | 二進制數據塊比較 |
char *str = "iphone6and8Plus23cc84";
int count = 0;
// 第一遍:統計數字個數
for (int i = 0; i < strlen(str); i++)
if (isdigit(str[i])) count++;
int *num = malloc(sizeof(int) * count); // 精確分配
count = 0;
// 第二遍:提取轉換
for (int i = 0; i < strlen(str); i++)
if (isdigit(str[i]))
num[count++] = str[i] - '0'; // '0'=48,比 48 可讀
// 使用後釋放
free(num); num = NULL;
free(ptr); ptr = NULL; —— 防雙重釋放、野指針strlen+1 分配calloc 避免未初始化讀取realloc 必須用臨時變量接收返回值,防失敗丟失原指針內存管理無小事,將五大區特性刻入肌肉記憶,才能在指針叢林中從容穿行。