您的位置:首页 >c++指针传递导致的重复释放问题解决
发布于2026-05-20 阅读(0)
扫一扫,手机访问
咱们先来看一段看似“严谨”的C++代码。作者的本意是好的:在多个地方都可能释放同一个指针时,通过判空来避免重复释放。想法很美好,但现实很骨感。

#includeusing namespace std; // 释放内存 void release(int *pPointer){ delete pPointer; pPointer = nullptr; } int main() { int *pPointer = new int(); // 释放 release(pPointer); if(pPointer) delete pPointer; return 0; }
运行一下,结果直接给了个下马威:
free(): double free detected in tcache 2 Process finished with exit code 134
问题来了:代码里明明写了if(pPointer)进行判空,为什么还是报“double free”(重复释放)的错误?这判空难道是摆设吗?
问题的根源,在于对C++函数参数传递机制的一个经典误解。很多人会下意识地认为,把指针传进函数,操作的就是“那个指针本身”。其实不然,这里发生了一次隐形的“拷贝”。
口说无凭,我们加点调试代码,把两个函数里指针变量自己的地址打印出来看看:
void release(int *pPointer){
printf("release pPointer:%xn",&pPointer); // 打印pPointer指针对象的地址
delete pPointer;
pPointer = nullptr;
}
int main() {
int *pPointer = new int();
printf("main pPointer:%xn",&pPointer); // // 打印pPointer指针对象的地址
// 释放
release(pPointer);
if(pPointer)delete pPointer;
return 0;
}
运行结果一目了然:
main pPointer:f22d1fb0 release pPointer:f22d1f98 free(): double free detected in tcache 2 Process finished with exit code 134
看到了吗?两个地址完全不同。这铁证如山:release函数里的pPointer,只是main函数中那个指针的一个“副本”。它们俩是独立的两个变量,只不过在函数调用之初,指向了同一块堆内存。
这样一来,整个悲剧的时间线就清晰了:
release函数里,delete pPointer;释放了堆内存,随后pPointer = nullptr;将这个“副本”指针置空。注意,这只影响了函数内部的这个副本。pPointer,对刚才发生的一切毫不知情。它依然固执地指向那块已经被释放的内存,成功晋级为“野指针”。if(pPointer) delete pPointer;时,判断条件为真(因为它不是nullptr),于是再次尝试释放同一块内存。这时,内存管理器就会抛出“double free”错误,程序崩溃。所以,判空操作本身没错,但它判的是main函数里的指针,而这个指针从未被置空过。问题的本质在于,我们希望通过函数去修改调用者的指针变量(将其置空),但普通的指针传参只提供了修改“指针所指内容”的能力,却没有提供修改“指针本身”的能力。
搞清楚了病因,开药方就简单了。核心目标就一个:让release函数有能力直接修改main函数里的那个指针变量,而不是修改它的副本。这里有两个经典的解决方案。
这是C++里更优雅、更直观的做法。直接把参数声明为指针的引用,这样传入的就是原指针变量本身,而非副本。
void release(int *& pPointer){ // 注意*后面的&
delete pPointer;
pPointer = nullptr;
}
其他代码完全不用动。再运行一下,世界清净了。我们同样可以验证两个函数中看到的指针地址现在是相同的:
main pPointer:c12d0580 release pPointer:c12d0580 Process finished with exit code 0
看,地址一模一样。这意味着在release函数里对pPointer的置空操作,直接作用到了main函数的变量上,后续的判空逻辑就能正确生效了。
这是更偏向C语言风格的解决方案,理解上需要多绕一个弯。既然想修改一个指针,那就传入这个指针的地址,也就是“指针的指针”。
void release(int **pPointer){ // 参数变为二级指针
printf("release pPointer:%xn",pPointer);
delete *pPointer; // 通过解引用*来操作原始指针指向的内存
*pPointer = nullptr; // 通过解引用*来将原始指针置空
}
int main() {
int *pPointer = new int();
printf("main pPointer:%xn",&pPointer);
// 释放 - 传入指针pPointer的地址
release(&pPointer);
if(pPointer)delete pPointer;
return 0;
}
运行结果同样成功:
main pPointer:d2699950
release pPointer:d2699950
Process finished with exit code 0
这里打印的release pPointer地址,其实就是main函数中pPointer变量的地址。函数内部通过解引用操作*pPointer,来访问和修改main函数里的那个一级指针。
简单来说,指针引用是“直接给变量起个别名”,而二级指针是“拿到变量的门牌号,然后按图索骥去修改它”。两者都能达到目的,但在现代C++中,指针引用的写法通常更受青睐,因为它语法更简洁,意图也更清晰。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8