delete p后,只是释放了指针中存放的地址中的内存空间。但是指针变量p仍然存在(即指针p本身所占有的内存),且p中存放的地址还是原来的地址。
例如:
对一个非空指针delete后,若没有将p赋为NULL,若再次delete的话,会出现问题。
如下代码:
#include <iostream> int main() { int* p = new int(3); delete p; delete p; return 0; }
在ubuntu14.04中使用g++进行编译无问题,但运行时报错如下:
意思就是对同一指针变量进行了两次释放内存的操作,这是不合法的。
因为第一次释放后,指针p指向的那块区域已经变为不可访问区域了,再执行一次delete p,试图对一块不可访问的区域进行释放,这是不合法的。
将其改为:
#include <iostream> int main() { int* p = new int(3); delete p; p = NULL; delete p; return 0; }
则编译和运行都没有问题,因为C++保证delete值为NULL的指针是安全的。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
具体的说明如下:
还是先看代码:
1 /* 2 *Compile Environment:linux ubuntu14.04.5 g++ 3 *Author: mengjia 4 *Date:20180520 5 **/ 6 #include <iostream> 7 using namespace std; 8 int main() 9 { 10 int *p = new int; 11 *p = 3; 12 cout << "将3赋给p的地址后,指针p读取的值:" << *p << endl; 13 cout << "删除空间前,指针p中存放的地址:" << p <<endl; 14 delete p; 15 cout<<"删除空间后,指针p存放的地址:"<< p <<endl; //在vs2017中,p中存放的地址会改变,但在g++中不会,应该是vs2017中对其进行优化了,让p指向了一个不可访问的地址,避免delete之后又误操作了p 16 cout << "删除空间后,指针p读取的值:" << *p << endl; //在vs2017中,对×p的访问直接会报错(引发了异常: 读取访问权限冲突),但在g++中,可 17 以正常往下执行。 18 long *p1 = new long; 19 *p1 = 100; 20 cout << "创建新空间后,指针p中存放的地址:" << p << endl; 21 cout << "指向新空间的指针p1存放的地址:" << p1 << endl; 22 *p = 23; 23 cout << "将23赋给p的地址后,指针p读取的值:" << *p << endl; 24 cout << "将23赋给p的地址后,指针p1读取的值:" << *p1 << endl; 25 delete p1; 26 // system("pause"); 27 return 0; 28 }
输出结果为:
从第13和第15行中可以看到,delete指针p前后,p中存放中地址并未改变。这就说明一个非常重要的结论:
delete一个指针后,编译器只是释放了指针中存放的地址中的内存空间,但p中存放的地址还是原来的地址。
在程序的第18行,创建了一个long型的指针p1,在20和21行的输出中发现,指针p保存的地址居然和指针p1保存的地址一毛一样。说明指针p和指针p1都指向内存的同一个地方。出现这种状态是因为编译器默认将释放掉的内存空间回收然后分配给新开辟的空间。所以18行,当开辟一个可以保存long型变量的空间并且由p1来指向它时,分配的空间为p指向的内存空间。
如此,将导致两个指针同时指向同一内存空间。这在C++中是非常忌讳的。
如上,在程序中定义了*p1 = 100;而后再操作p,使*p = 23;而后读取p1指向的内存中的值,变为了23。这就会使得已经delete的指针,若操作不当,会影响到程序的其它指针。这种情况就是由于野指针p造成的。
要避免这种情况的发生,解决办法就是:
在删除一个指针之后,一定要将该指针设置成空指针,即在delete p之后,要加上: p = NULL
补充说明:
请注意程序中第15行和16行的注释,这段程序在vs2017上是编译不通过的。原因就如注释所言,vs2017对其进行了优化,delete之后,让p的指向改变了,改变为指向一个不可访问的地址,使得之后如果有任何*p相关的操作,都会直接报错。其目的,我觉得就是保证在源头断绝,不让出现野指针的情况。
但这种依赖于编译器的优化,对我们理解C++并没有好处。