動態內存管理四大函數
| 函數 | 用途 | 關鍵差異 |
malloc(size) | 分配未初始化內存 | 內容隨機,需手動清零 |
calloc(n, size) | 分配並歸零 | 等價 malloc + memset(0) |
realloc(ptr, new_size) | 調整已分配塊大小 | 可能移動地址,必須用返回值更新指針 |
free(ptr) | 釋放內存 | 釋放後指針成野指針,建議置 NULL |
代碼示例速覽
// 10.1 malloc
int *p = (int *)malloc(sizeof(int));
*p = 100; printf("%d\n", *p); free(p);
// 10.2 calloc 分配字符數組
char *ch1 = (char *)calloc(26, sizeof(char)); // 自動歸零
char *ch2 = (char *)malloc(26);
for (int i=0;i<26;i++) { ch1[i]=65+i; ch2[i]=97+i; } // 大小寫字母
// 10.3 realloc 擴容/縮容
double *f = (double *)malloc(sizeof(double));
short *s = (short *)realloc(f, sizeof(short)); // 必須接收返回值!
// 10.4 free 釋放後訪問是未定義行為
int *pInt = (int *)malloc(sizeof(int));
*pInt = 100; free(pInt); printf("%d\n", *pInt); // 輸出不可預期
鏈表核心操作三部曲
1. 創建並輸出(例 10.5)
struct Student { char cName[20]; int iNumber; struct Student *next; };
struct Student *Create() {
struct Student *pHead=NULL, *pEnd, *pNew;
pEnd = pNew = (struct Student *)malloc(sizeof(struct Student));
scanf("%s%d", pNew->cName, &pNew->iNumber);
while (pNew->iNumber != 0) {
if (pHead==NULL) { pNew->next=pHead; pHead=pNew; }
else { pNew->next=NULL; pEnd->next=pNew; pEnd=pNew; }
pNew = (struct Student *)malloc(sizeof(struct Student));
scanf("%s%d", pNew->cName, &pNew->iNumber);
}
free(pNew); return pHead; // 釋放最後多分配的節點
}
2. 頭插法插入(例 10.6)
struct Student *Insert(struct Student *pHead) {
struct Student *pNew = (struct Student *)malloc(sizeof(struct Student));
scanf("%s%d", pNew->cName, &pNew->iNumber);
pNew->next = pHead; // 新節點指向原頭
return pNew; // 新節點成為新頭
}
3. 按序號刪除(例 10.7)
void Delete(struct Student *pHead, int iIndex) {
struct Student *pTemp = pHead, *pPre = pHead;
for (int i=1; inext; }
pPre->next = pTemp->next; // 旁路被刪節點
free(pTemp); // 釋放內存
}
4. 兩有序鏈表合併(例 10.8)
void merge(struct Student *La, struct Student *Lb) {
struct Student *a=La->next, *b=Lb->next, *c=La;
while (a && b) {
if (a->iNumber <= b->iNumber) { c->next=a; c=a; a=a->next; }
else { c->next=b; c=b; b=b->next; }
}
c->next = a ? a : b; // 尾部拼接
free(Lb); // 釋放 Lb 頭節點
}
高頻錯誤避坑指南
- malloc 未判空 → 分配失敗直接解引用崩潰
- realloc 直接覆蓋原指針 →
p = realloc(p, ...) 若失敗返回 NULL,原內存洩漏;正確寫法 void *tmp = realloc(p, ...); if (tmp) p = tmp;
- free 後不置 NULL → 野指針二次 free 或誤用
- 鏈表遍歷修改 next 指針順序錯 → 先保存
next 再 free
- scanf 格式串
%s 不限長度 → 緩衝區溢出,用 %19s
練習建議
- 手寫「雙向鏈表反轉」「單鏈表找中點」「檢測環」
- Valgrind 檢測內存洩漏:
valgrind --leak-check=full ./a.out
- 閱讀 Linux 內核
list_head 實現,理解無侵入式鏈表設計