C++如何修改func函数删除自定义节点后将关联指针置为nullptr
C++自定义链表节点删除问题修复
问题复现
实现链表节点删除逻辑时,初始代码无法正确置空关联指针,最终两行指针判断均输出0(false)。题目要求仅修改func()函数、仅通过传入的root指针完成nxt节点删除,让两行判断输出1(true)。
初始错误代码如下:
class node { public: node* next; int val; }; void func(node* root) { node* p=root->next; delete p; p=nullptr; } int main() { node* root=new node(); node* nxt=new node(); root->val=1;root->next=nxt; nxt->val=2;nxt->next=nullptr; func(root); cout<<(nxt==nullptr)<<endl;// 实际输出0 cout<<(root->next==nullptr)<<endl;// 实际输出0 }
错误原因分析
很多初学者会存在认知误区:以为执行root->next = nxt赋值后,root->next和nxt是同一个指针对象。实际上二者是存储在完全独立内存空间的两个指针,只是保存的内存地址值相同,都指向val=2的第二个节点:
root->next是root指向的堆上节点对象的内部成员,内存地址在堆区nxt是main函数栈帧上的局部变量,内存地址在栈区
可以通过以下代码验证二者的差异:
int main() { node* root=new node(); node* nxt=new node(); root->val=1;root->next=nxt; nxt->val=2;nxt->next=nullptr; // 两个指针保存的地址值相同,都指向第二个节点 cout<<(root->next)<<endl; cout<<(nxt)<<endl; cout<<"---------"<<endl; // 两个指针自身的存储地址完全不同 cout<<&(root->next)<<endl; cout<<&nxt<<endl; }
运行输出示例:
0x7219b0 0x7219b0 --------- 0x7219a0 0x61ff08
你观察到的地址差0x10是正常现象:64位系统下指针占8字节,node结构体内存对齐后总大小为16字节,两次连续调用new时堆管理器分配了连续的内存块,第一个节点(root指向的节点)占0x7219a0~0x7219af的16字节,第二个节点从0x7219b0开始分配,刚好差0x10,没有特殊逻辑含义。
原func()函数的核心错误是对C++值传递语义理解不到位:
- 函数内定义的局部指针
p只是root->next的副本,将p置空仅修改函数栈上的局部变量,不会影响外部的root->next和main函数内的nxt指针 - 释放p指向的堆内存后,没有修改前驱节点root的next指针,导致
root->next和nxt都成为悬空野指针,自然不会等于nullptr
正确修改方案
func()函数实现(符合题目操作约束)
按照仅在func内通过root指针操作的要求,可修改为如下实现:
void func(node* root) { // 暂存要删除的节点地址 node* to_del = root->next; // 断开前驱节点与待删除节点的链接,将root->next置空 root->next = nullptr; // 释放待删除节点的堆内存 delete to_del; to_del = nullptr; }
修改后root->next == nullptr的判断会输出1,符合链表删除的标准逻辑:删除节点后必须修改前驱节点的next指针,避免野指针。
关于nxt指针判断的说明
在当前给定的函数签名(仅接收node* root参数)下,没有合法途径可以修改main函数栈上的局部变量nxt的值:nxt的内存位于main的栈帧中,root指向的堆内存无法直接访问到栈上的该变量地址,强行通过野指针跨栈内存修改变量属于未定义行为,不具备可移植性,严重时会导致程序崩溃。
如果需要让nxt == nullptr的判断也输出1,必须修改函数签名,传入nxt的引用或者二级指针,但这不符合题目“仅通过root指针操作”的约束。在实际链表实现中,不会在遍历逻辑外单独保存中间节点的指针副本,所有节点访问都通过链表头节点遍历,因此只要正确修改前驱节点的next指针,就不会出现野指针访问问题。
内容的提问来源于stack exchange,提问作者sakura
相关产品推荐
相关产品推荐

