帶頭結點的單向鏈表,插入/刪除第一個資料節點時,為何函數參數仍需 `Node** head` (二級指針)?本文從記憶體模型圖解指針修改的本質差異。
即使有頭結點,若函數內部可能修改「頭結點的 `next` 指針指向新節點」,且呼叫端需感知此修改,仍需傳遞頭結點指針的地址 (二級指針) 或返回新頭指針。
| 方式 | 函數簽名 | 呼叫端 | 適用場景 |
|---|---|---|---|
| 二級指針 | void Insert(Node** head, int val) | Insert(&head, 10); | C 風格、需修改頭結點 `next` 且不想返回值 |
| 返回新頭指針 | Node* Insert(Node* head, int val) | head = Insert(head, 10); | 現代 C/C++ 風格、鏈表可能無頭結點/頭結點本身被替換 |
// 帶頭結點結構
Node* head = malloc(sizeof(Node)); // head 指向頭結點 (不存資料)
head->next = NULL; // 空表
// 插入第一個資料節點
Node* newNode = malloc(sizeof(Node));
newNode->data = 10;
newNode->next = head->next; // NULL
head->next = newNode; // ★ 修改頭結點的 next 指針
此處 `head` (一級指針) **本身未變**,但 `head->next` **指向變了**。若函數接收 `Node* head` (傳值),函數內修改 `head->next` **對呼叫端可見** (因指向同一頭結點記憶體)。**但若函數需修改 `head` 本身** (如刪除頭結點、整表反轉換新頭結點),則必須用二級指針或返回值。
「有頭結點就不需要二級指針」——錯誤。頭結點解決的是「空表/首節點操作統一邏輯」,不解決「函數修改呼叫端指針變數」的傳參問題。
Node* Insert(Node* head, int val),呼叫端 `head = Insert(head, val);`,語意清晰、無二級指針心智負擔。typedef struct { Node* head; int size; } List;
void ListInsert(List* list, int val) { list->head = Insert(list->head, val); list->size++; }
掌握指針修改的記憶體語義,鏈表操作不再迷茫。