C++双向链表头插后执行尾插触发未处理空指针异常问题
问题现象
C++实现双向链表时,单独执行头部插入、单独执行尾部插入逻辑均运行正常,但先执行头部插入再执行尾部插入时,会触发空指针访问错误,异常定位在InsertAtEnd函数内。
原实现代码如下:
#include <iostream> using namespace std; struct Node { int data; Node* prev; Node* next; }; struct MyList { Node* head; Node* tail; }; bool IsEmpty(MyList list) { if (list.head == nullptr) return true; else return false; } void Insert(MyList& list, int data) { Node* node = new Node(); node->data = data; node->prev = nullptr; node->next = list.head; //node->next points to NULL if (list.head == nullptr) { list.head = node; node->prev = nullptr; } else { //insert the new node at the beginning of the list list.head->prev = node; list.head = node; node->prev = nullptr; } } // Insert node at end of list void InsertAtEnd(MyList& list, int data) { Node* node = new Node(); node->data = data; node->next = nullptr; node->prev = nullptr; if (list.head == nullptr) { // Empty list list.head = node; list.tail = node; } else { list.tail->next = node; node->prev = list.tail; list.tail = node; } } //Traverse the list from the head void PrintAll(const MyList& list) { Node* temp = list.head; if (temp == nullptr) { cout << "list is empty" << endl; } else { while (temp != nullptr) { cout << temp->data << endl; temp = temp->next; } cout << "*************************************" << endl; } } Node* Search(const MyList& list, int key) { Node* temp = list.head; while (temp != nullptr && temp->data != key) { temp = temp->next; } return temp; } void Delete(MyList& list, int key) { Node* temp = Search(list, key); //call search() if (temp != nullptr) { if (temp->prev != nullptr) { temp->prev->next = temp->next; } else { list.head = temp->next; } if (temp->next != nullptr) { temp->next->prev = temp->prev; } } } int main() { MyList list; list.head = nullptr; //initialize the linked-list list.tail = nullptr; if (IsEmpty(list)) cout << "List is empty" << endl; Insert(list, 10); PrintAll(list); /* Insert(list, 20); Insert(list, 30); Insert(list, 40); */ // Insert at end cout << "Now insert at end" << endl; InsertAtEnd(list, 70); InsertAtEnd(list, 45); InsertAtEnd(list, 59); InsertAtEnd(list, 12); InsertAtEnd(list, 33); PrintAll(list); /* int x = 24; Node* result = Search(list, x); if (result == nullptr) cout << "Cannot find " << x << endl; else cout << "Found " << result->data << endl; Delete(list, 200); PrintAll(list); Delete(list, 10); PrintAll(list); Delete(list, 40); PrintAll(list); Delete(list, 20); PrintAll(list); Delete(list, 30); PrintAll(list); */ return 0; }
根因分析
空指针错误的核心原因是链表头尾指针状态不一致:
- 头部插入函数
Insert在空链表中插入第一个节点时,只更新了head指针,完全没有同步设置tail指针。首次调用Insert(list,10)后,list.head指向值为10的节点,但list.tail仍然是初始化时的nullptr - 后续调用
InsertAtEnd时,判断list.head != nullptr就进入非空分支,直接访问list.tail->next,此时list.tail是空指针,直接触发内存访问错误。 - 原实现的
Delete函数还存在两个隐藏问题:删除尾节点时没有更新tail指针、删除节点后没有释放内存,会在后续操作中触发同类崩溃和内存泄漏。
修复方案
- 修复头部插入函数,空链表插入首节点时同步更新
tail指针:
void Insert(MyList& list, int data) { Node* node = new Node(); node->data = data; node->prev = nullptr; node->next = list.head; if (list.head == nullptr) { list.head = node; list.tail = node; // 空链表插入首节点时同步设置tail node->prev = nullptr; } else { list.head->prev = node; list.head = node; node->prev = nullptr; } }
- 修复删除函数,补充尾节点更新逻辑和内存释放:
void Delete(MyList& list, int key) { Node* temp = Search(list, key); if (temp != nullptr) { if (temp->prev != nullptr) { temp->prev->next = temp->next; } else { list.head = temp->next; } if (temp->next != nullptr) { temp->next->prev = temp->prev; } else { // 删除的是尾节点时,更新tail指针 list.tail = temp->prev; } delete temp; // 释放节点内存,避免泄漏 } }
修复后链表的head和tail指针在所有插入、删除场景下都能保持状态一致,不会再触发空指针错误。
内容的提问来源于stack exchange,提问作者Bill Moran
相关产品推荐
相关产品推荐

