C++链表删除重复节点时出现指针访问冲突(0xDDDDDDDD)的原因排查咨询
看起来你在实现链表重复节点删除的时候碰到了内存访问的坑,我来帮你拆解问题根源,再给出可运行的修正代码~
为什么会出现read access violation?
你看到的ptr2变成0xDDDDDDDD是微软VC编译器的特殊标记——它表示这块内存已经被释放了。问题主要出在这几个地方:
内层循环条件写漏了最后一个节点:
你写的while (ptr2 != NULL && ptr2->next != NULL)会直接跳过链表的最后一个节点。比如当ptr2指向最后一个20时,ptr2->next是NULL,循环直接终止,根本不会检查这个节点和ptr1是否重复。删除节点时没维护链表的链接:
当你找到重复节点ptr2时,直接delete(dup)但没修改前一个节点的next指针。举个例子:假设链表是ptr1 -> ptr2 -> next_node,你删了ptr2之后,ptr1->next依然指向已经被释放的ptr2内存,后续遍历的时候访问这个地址就会触发内存访问违规。而且你直接把ptr2改成ptr2->next,原来的链表链接已经断了,下次循环必然踩坑。内层循环没跟踪前一个节点:
你每次内层循环从ptr1->next开始,但没记录ptr2的前一个节点,导致删完节点后没法把前后节点连起来,链表直接断成好几段。
修正后的完整代码
我把你的代码补全了辅助函数(push、printList、deleteList),同时修复了核心逻辑:
#include <iostream> using namespace std; class Node { public: int data; Node *next; }; // 在链表头部插入节点的辅助函数 void push(Node** head_ref, int new_data) { Node* new_node = new Node(); new_node->data = new_data; new_node->next = (*head_ref); (*head_ref) = new_node; } // 打印链表的辅助函数 void printList(Node *node) { while (node != NULL) { cout << node->data << " "; node = node->next; } cout << endl; } // 销毁整个链表的辅助函数 void deleteList(Node** head_ref) { Node* current = *head_ref; Node* next; while (current != NULL) { next = current->next; delete current; current = next; } *head_ref = NULL; } void removeDuplicatesAlpha(Node* start) { Node* ptr1 = start; Node* ptr2; Node* dup; // 外层循环逐个遍历每个节点 while (ptr1 != NULL && ptr1->next != NULL) { ptr2 = ptr1; // 让ptr2从ptr1开始,跟踪当前节点的前一个节点 // 内层循环遍历ptr1之后的所有节点 while (ptr2->next != NULL) { // 如果找到和ptr1重复的节点 if (ptr1->data == ptr2->next->data) { dup = ptr2->next; // 先把前一个节点的next指向重复节点的下一个,维护链表链接 ptr2->next = ptr2->next->next; delete dup; // 释放重复节点的内存 } else { ptr2 = ptr2->next; // 没重复就移动到下一个节点 } } ptr1 = ptr1->next; } } int main() { Node* head = NULL; push(&head, 3); push(&head, 3); push(&head, 20); push(&head, 14); push(&head, 9); push(&head, 20); push(&head, 20); cout << "原链表: "; printList(head); removeDuplicatesAlpha(head); cout << "删除重复后的链表: "; printList(head); deleteList(&head); return 0; }
关键修复点说明
修正内层循环逻辑:
把内层循环改成while (ptr2->next != NULL),这样能遍历到链表的最后一个节点,不会漏掉任何重复项。同时让ptr2从ptr1开始,方便跟踪前一个节点,删除时能正确维护链表链接。正确维护链表链接:
发现重复节点时,先让前一个节点的next跳过重复节点,再删除重复节点,这样链表不会断裂,也不会出现指向已释放内存的情况。彻底避免访问已释放内存:
不再直接修改ptr2为已删除节点的next,而是通过前一个节点的next来跳转,从根源上避免了踩0xDDDDDDDD这类已释放内存的坑。
运行修正后的代码,你会得到正确的输出:
原链表: 20 20 9 14 20 3 3 删除重复后的链表: 20 9 14 3
内容的提问来源于stack exchange,提问作者Alexandru Manolache

