前陣子抱著「打醬油」的心態參加了學校某社團的幹部面試,沒想到卻被幾個看似簡單的 C 語言問題卡住了。面試結束後,我花了一整天時間徹底搞懂了這些問題,這才發現:平時多去面試還真有好處,至少能精準定位自己的技術盲區。今天就來深入剖析其中一道關於記憶體分配的經典面試題。
請先看這段代碼:
void allocmem(char **p)
{
char r[10] = {1};
*p = r;
}
int main()
{
int i;
char *m = NULL;
allocmem(&m);
if (m)
{
for (i = 0; i < 10; i++)
printf("m[%d] = %d\n", i, m[i]);
}
else
{
printf("alloc mem failed\n");
}
return 0;
}

運行結果顯示數據已經損壞,出現亂碼。為什麼?因為 r[10] 是 區域變數(棧上分配),函數 allocmem 返回後,棧幀銷毀,記憶體被系統回收,指標 m 變成了懸空指標,指向的內容不可預測。
再看這段代碼:
void allocmem(char **p)
{
*p = malloc(10);
memset(*p, 1, 10);
}
int main()
{
int i;
char *m = NULL;
allocmem(&m);
if (m)
{
for (i = 0; i < 10; i++)
printf("m[%d] = %d\n", i, m[i]);
}
else
{
printf("alloc mem failed\n");
}
return 0;
}

這次輸出正常,全是 1。關鍵差異在於 malloc 在 堆上分配記憶體,函數結束不會自動釋放,必須由程式設計師手動 free。
| 特性 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 區域變數、自動分配 | malloc/free、手動管理 |
| 生命週期 | 隨函數調用結束自動釋放 | 需手動釋放,否則洩漏 |
| 空間大小 | 較小(通常 MB 級) | 較大(受限於虛擬記憶體) |
| 分配速度 | 極快(指針移動) | 較慢(需尋找合適塊) |
避坑指南: 永遠不要返回區域變數的地址!若需跨函數使用記憶體,請使用動態分配(malloc/new)或靜態/全域變數。