自己管記憶體
要多少自己開,用完自己還。 · ⏱ 約 35 分鐘
你會學到三件事
malloc 在執行時才決定要多大
free 用完要還——不還就是漏
sizeof 問「這個型別佔幾個位元組」
開始之前
C 的陣列大小要在寫程式的時候就決定:
int a[5]; // 五格,寫死
而「要幾格」常常要跑起來才知道。C++ 用 vector,C 用 malloc。
一、開一塊
int *a = (int*)malloc(5 * sizeof(int));
拆開來看:
| 這一段 |
意思 |
sizeof(int) |
一個 int 佔幾個位元組(多半是 4) |
5 * sizeof(int) |
我要 5 個 int 的空間 = 20 位元組 |
malloc(20) |
給我 20 個位元組,回傳它的地址 |
(int*) |
把那個地址當成「指向 int 的指標」 |
⚠️ malloc 要的是位元組數,不是格數。
寫 malloc(5) 的話你只拿到 5 個位元組——一個 int 都放不滿兩個。
⚠️ 不要寫死 malloc(5 * 4)。 int 不一定是 4 個位元組,
而 sizeof 永遠是對的。
二、用起來跟陣列一樣
for (int i = 0; i < 5; i++) {
a[i] = i * i;
}
printf("%d\n", a[4]); // 16
方括號對指標一樣能用——因為 a[i] 的定義就是 *(a + i)。
陣列和指標在 C 裡幾乎是同一件事。
差別在於陣列的空間是編譯器給的,malloc 的是你自己要的。
三、🔴 用完要還
free(a);
不還的話那塊空間一直被佔著,直到程式結束。
一支跑一秒的程式漏一點沒差;一支跑一整天的伺服器漏一點,
一天之後記憶體就滿了。這叫記憶體洩漏。
⚠️ free 之後不要再用它:
free(a);
printf("%d\n", a[0]); // 🔴 那塊空間已經不是你的了
這叫 use-after-free,而它不一定會當掉——它可能印出一個看起來正常的數字,
因為那塊記憶體還沒被別人拿走。這是最難查的一類 bug。
習慣:free(a); 之後馬上 a = NULL;。這樣再用到就會當場當掉,而不是安靜地錯。
四、⚠️ malloc 可能失敗
記憶體不夠的時候 malloc 回 NULL。
int *a = (int*)malloc(n * sizeof(int));
if (a == NULL) {
printf("記憶體不夠\n");
return 1;
}
教學程式裡常常省略,而正式的程式不能省。
完成的樣子
int main() {
int *a = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++) {
a[i] = i * i;
}
printf("%d\n", a[4]);
free(a);
return 0;
}
換你了
改成讀進使用者輸入的 n,開 n 格,填 1 到 n,印出總和。
這就是 vector 幫你做的事。 做完之後你會知道那顆積木背後是什麼。
這一課你做了什麼
- 你在執行時才決定了一塊記憶體要多大
- 你用
sizeof 算出了需要的位元組數
- 你
free 了它,並知道不還會怎樣
如果卡住了
| 你看到 |
多半是因為 |
| 只有前一兩格是對的 |
malloc 的參數忘了乘 sizeof(int) |
free 之後還印得出東西 |
那是巧合。那塊記憶體已經不是你的了 |
| 程式跑久了越來越慢 |
漏了 free |
| double free 錯誤 |
同一塊 free 了兩次。free 完設 NULL |
在編輯器打開這一課 →