商城首页欢迎来到中国正版软件门户

您的位置:首页 >c++指针传递导致的重复释放问题解决

c++指针传递导致的重复释放问题解决

  发布于2026-05-20 阅读(0)

扫一扫,手机访问

first

咱们先来看一段看似“严谨”的C++代码。作者的本意是好的:在多个地方都可能释放同一个指针时,通过判空来避免重复释放。想法很美好,但现实很骨感。

c++指针传递导致的重复释放问题解决

#include 
using 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函数中那个指针的一个“副本”。它们俩是独立的两个变量,只不过在函数调用之初,指向了同一块堆内存。

这样一来,整个悲剧的时间线就清晰了:

  1. release函数里,delete pPointer;释放了堆内存,随后pPointer = nullptr;将这个“副本”指针置空。注意,这只影响了函数内部的这个副本。
  2. 函数返回后,main函数里的那个“正主”指针pPointer,对刚才发生的一切毫不知情。它依然固执地指向那块已经被释放的内存,成功晋级为“野指针”。
  3. 程序执行到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++中,指针引用的写法通常更受青睐,因为它语法更简洁,意图也更清晰。

本文转载于:https://www.jb51.net/program/36414549f.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注