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