first
先看这段代码,有多个地方都会释放 pPointer 的对象空间,但是为了防止重复释放,在第二次释放时做了判空处理;不为空才释放;
#include <iostream>
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;
}
执行结果如下, 报错了,告诉我执行了2次free函数,也就是重复释放了;这是怎么回事呢? 我明明有做判空啊;
free(): double free detected in tcache 2
Process finished with exit code 134
原因
原来;在调用release函数时,传入的指针变量,实际是经过了拷贝的;不信是吧,我们来做个实验,在main函数和release函数都打印下 pPointer 对象的地址,代码如下
void release(int *pPointer){
printf("release pPointer:%x\n",&pPointer); // 打印pPointer指针对象的地址
delete pPointer;
pPointer = nullptr;
}
int main() {
int *pPointer = new int();
printf("main pPointer:%x\n",&pPointer); // // 打印pPointer指针对象的地址
// 释放
release(pPointer);
if(pPointer)delete pPointer;
return 0;
}
执行结果如下, 可以看到 2个指针地址是不同的,这也就证明了,在传参的过程中是发生了拷贝的;release 函数中的 pPointer 对象和main函数中的 pPointer 对象并不是同一个对象,只是他们都指向了同一块内存。
main pPointer:f22d1fb0
release pPointer:f22d1f98
free(): double free detected in tcache 2
Process finished with exit code 134
所以,他们的时间线如下:
- 在 release 函数内释放 pPointer, 并将 pPointer 赋值为 nullptr,此时,pPointer 所指向的内存块已经被释放,
- 但是只是将 release 函数内的 pPointer 变量赋值为 nullptr,main函数中 pPointer 还是指向原来的地址;因为指针指向的内存块已被释放,此时 main 函数中 pPointer 就是个野指针;
- 当指执行到
if(pPointer) delete pPointer;
代码时,pPointer 并不是空指针 nullptr ,它还指向原来的地址,所以判断if(pPointer)
的结果为真(true),然后会执行delete pPointer;
,但是原来地址的空间已经在release函数释放了,执行delete pPointer;
后就会报错:double free detected in tcache 2
;
解决
方案一 :使用指针引用
将release函数的参数改为指针引用,如下,注意看,*
后面多了个&
符号,其他代码无需改动
void release(int *& pPointer){
delete pPointer;
pPointer = nullptr;
}
再次执行后,就不会报错了, 且main函数和release函数的 pPointer 对象地址也是相同的
main pPointer:c12d0580
release pPointer:c12d0580
Process finished with exit code 0
方案二:使用二级指针
将release函数的参数改为二级指针,也能解决问题,如下
void release(int **pPointer){
printf("release pPointer:%x\n",pPointer);
delete *pPointer; // 这里需要解引用
*pPointer = nullptr; // 这里需要解引用
}
int main() {
int *pPointer = new int();
printf("main pPointer:%x\n",&pPointer);
// 释放 - 需要加上取地址符号
release(&pPointer);
if(pPointer)delete pPointer;
return 0;
}
执行后结果如下
main pPointer:d2699950
release pPointer:d2699950
Process finished with exit code 0