陣列大小必須編譯期確定,無法應對執行期資料量未知的情況。C 語言提供 malloc/calloc/free 三大動態記憶體函式,配合結構體指標即可構建鏈結串列、樹、圖等彈性資料結構。本文完整涵蓋動態配置 API、鏈結串列四大基本操作(建立、遍歷、插入、刪除),並提供生產級錯誤處理模式。
| 函式 | 原型 | 行為 | 典型用法 |
|---|---|---|---|
malloc | void *malloc(size_t size) | 分配 size 位元組,內容未初始化 | p = malloc(sizeof(Node)) |
calloc | void *calloc(size_t n, size_t size) | 分配 n*size 位元組,歸零 | p = calloc(100, sizeof(int)) |
free | void free(void *ptr) | 釋放 ptr 指向的區塊,ptr 必須來自 malloc/calloc/realloc | free(p); p = NULL; |
鐵律:malloc 失敗回傳 NULL,必須檢查;free 後指標懸空,立即置 NULL 防雙重釋放。
struct Node {
int num; // 資料域:學號
int age; // 資料域:年齡
struct Node *next; // 指標域:指向下一節點
};
typedef struct Node Node;
typedef Node *List; // 方便閱讀
List create_list(int n) {
List head = malloc(sizeof(Node)); // 哨兵頭節點
if (!head) return NULL;
head->next = NULL;
List tail = head;
for (int i = 0; i < n; ++i) {
List node = malloc(sizeof(Node));
if (!node) { destroy_list(head); return NULL; }
printf("輸入學號 年齡: ");
scanf("%d %d", &node->num, &node->age);
node->next = NULL;
tail->next = node;
tail = node;
}
return head; // 呼叫端負責銷毀
}
void print_list(List head) {
for (List p = head->next; p; p = p->next)
printf("學號:%d 年齡:%d\n", p->num, p->age);
}
bool list_insert(List head, int pos, int num, int age) {
List prev = head;
for (int i = 0; i < pos && prev; ++i)
prev = prev->next;
if (!prev) return false; // 越界
List node = malloc(sizeof(Node));
if (!node) return false;
node->num = num; node->age = age;
node->next = prev->next;
prev->next = node;
return true;
}
bool list_erase(List head, int pos) {
List prev = head;
for (int i = 0; i < pos && prev->next; ++i)
prev = prev->next;
if (!prev->next) return false; // 越界
List victim = prev->next;
prev->next = victim->next;
free(victim);
return true;
}
void destroy_list(List head) {
List p = head;
while (p) {
List tmp = p->next;
free(p);
p = tmp;
}
}
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
struct Node { int num, age; struct Node *next; };
typedef struct Node Node;
typedef Node *List;
List create_list(int n);
void print_list(List head);
bool list_insert(List head, int pos, int num, int age);
bool list_erase(List head, int pos);
void destroy_list(List head);
int main() {
int n;
printf("輸入學生人數: ");
scanf("%d", &n);
List head = create_list(n);
if (!head) { fprintf(stderr, "記憶體不足\n"); return 1; }
printf("\n--- 初始名單 ---\n");
print_list(head);
list_insert(head, 1, 999, 20); // 第 2 位插入
printf("\n--- 插入後 ---\n");
print_list(head);
list_erase(head, 0); // 刪除第 1 位
printf("\n--- 刪除後 ---\n");
print_list(head);
destroy_list(head);
return 0;
}
/* 以上函式實作同前節 */
| 錯誤 | 後果 | 修正 |
|---|---|---|
| 未檢查 malloc 回傳值 | 解參考 NULL → 崩潰 | 每次配置後 if (!p) handle_error(); |
| free 後未置 NULL | 懸空指標、雙重釋放 | free(p); p = NULL; |
| 忘記釋放 | 記憶體洩漏 | 配對 malloc/free,用 valgrind 檢測 |
| 釋放非堆疊記憶體 | 未定義行為 | 只釋放 malloc/calloc/realloc 回傳的指標 |
| 結構體指標域未初始化 | 亂碼指標 → 野指標 | node->next = NULL; 或 calloc 歸零 |
prev 指標,支援 O(1) 反向遍歷與節點自我移除。next 指向頭節點,適用循環排程、約瑟夫問題。malloc 大塊記憶體,自行切分節點,減少系統呼叫開銷、提升快取局部性。掌握 malloc/free 與鏈結串列四大操作,是從「寫出能跑的程式」邁向「寫出健壯、高效、可維護系統程式」的關鍵門檻。