初學 C/C++ 實現單鏈表時,最常見的錯誤是在 creat 函數中試圖修改主函數的頭結點指針,卻傳遞了指針的值而非地址,導致主函數的頭指針永遠不會被更新。本文透過您的代碼詳細剖析此問題並給出正確寫法。
void creat(LNode* L) { // 參數是指針的「副本」
List p = (List)malloc(sizeof(LNode));
L = p; // 修改的是副本,主函數的 L 不受影響
// ...
}
void main() {
LNode L; // 棧上分配的結構體變量
creat(&L); // 傳遞 L 的地址 (LNode*)
get(&L);
}
關鍵誤區:LNode L 是一個實體結構體,非指針。&L 的類型是 LNode* (即 List),傳入 creat(List L) 後,函數內部的 L 是這個地址的副本。當你在函數內做 L = p 時,只是讓局部副本指向新記憶體,主函數的 L 仍指向原棧記憶體,且其 next 從未被初始化,導致鏈表斷裂。
void creat(List *L) { // 參數類型改為 List* (即 LNode**)
*L = (List)malloc(sizeof(LNode)); // 解引用修改主函數的指針值
List p = *L;
// ... 後續節點插入邏輯
}
void main() {
List L = NULL; // 定義為指針並初始化為 NULL
creat(&L); // 傳遞指針的地址
get(L); // 列印時直接傳指針值
}
void creat(List &L) { // 引用參數,直接操作主函數的 L
L = (List)malloc(sizeof(LNode));
// ...
}
void main() {
List L = NULL;
creat(L); // 呼叫時無需取址
get(L);
}
get 的邏輯修正您原本的列印邏輯有兩個問題:
p->next = p 造成自我循環,應為 p->next = NULL (尾插法需維護尾指針)。L 開始卻印 p->next->data,會漏掉第一個有效節點或讀取垃圾值。void get(List L) { // 傳值即可,不需修改頭指針
List p = L;
while (p != NULL) {
printf("節點數據:%d\n", p->data);
p = p->next;
}
}
#include <iostream>
#include <cstdlib>
using namespace std;
typedef struct LNode {
int data;
LNode* next;
} LNode, *List;
void creat(List &L) {
L = (List)malloc(sizeof(LNode));
L->next = NULL;
List tail = L;
int n, val;
cout << "輸入1繼續添加,0結束: ";
cin >> n;
while (n) {
cout << "輸入節點數據: ";
cin >> val;
List p = (List)malloc(sizeof(LNode));
p->data = val;
p->next = NULL;
tail->next = p;
tail = p;
cout << "輸入1繼續添加,0結束: ";
cin >> n;
}
// 移除哨兵頭節點 (若不需要)
List head = L;
L = L->next;
free(head);
}
void get(List L) {
for (List p = L; p != NULL; p = p->next)
cout << "節點數據:" << p->data << endl;
}
int main() {
List L = NULL;
creat(L);
get(L);
return 0;
}
| 場景 | 參數類型 | 函數內修改頭指針 | 主函數調用 |
|---|---|---|---|
| 只需修改節點內容 | List L (指針值) | L->data = ... | creat(head) |
| 需修改頭指針指向 (如建表、插入頭部) | List &L (引用) 或 List *L (雙重指針) | L = newNode 或 *L = newNode | creat(head) 或 creat(&head) |
掌握「指針傳值 vs 引用/雙重指針」的區別,是玩轉 C/C++ 鏈表、樹等動態資料結構的關鍵。