简单说来,指针最郁闷的是,给你一个指针你不知道它实际指的是什么。例如一个指针int* p;,它是像int ival=23; int* p=&ival这样指向单个变量,还是像int a[3]={1,2,3}; int* p=a;这样指向一个数组?或许它还根本什么东西也没有指,是个空指针。就算它指向一个数组,但你知道它指向的数组长度吗?不知道吧,所以我们会经常乱指,指来指去冷不防就指出界了。
当然,指向动态分配的空间的指针更让人防不甚防,一不小心你就会被冠上内存泄露的罪名,这很冤,因为你从来都没想过要泄露内存。所以差不多所有正常的C++编程书都会告诫我们,动态分配空间后一定要记得释放!但释放空间的时候问题又来了。
第一个问题是如果指针指向单个动态空间,你就得像这样delete p;如果指针指向动态数组,你就得像这样delete[] p;。如果你在第二种情况中采用第一种情况中的策略,那你又把内存泄露了。
第二个问题是那些写一个函数可以写几百几千行的人可能出现的问题,因为他们可能分配的空间有点多,最后竟忘了正在使用的这个指针前面已经释放过了,导致非法访问本已释放的内存空间。现在我们来模拟这种情况:(值得注意的是这个程序虽然不正确但不会报任何错误!)
void function() {
int rows=4;
int i;
int* p=new int[rows];
for(i=0; i<rows; ++i) {
p[i]=i;
}
for(i=0; i<rows; ++i) {//第一次正常打印
cout<<p[i]<<"/t";
}
cout<<endl;
//几百行代码...
delete[] p;
//又是几百行代码...
for(i=0; i<rows; ++i) {//第二次打印非法(会打印出随机数因为释放了的内存的状态不再受本程序的支配)
cout<<p[i]<<"/t";
}
cout<<endl;
for(i=0; i<rows; ++i) {//非法修改内存(可能把其它程序的内存修改了)
p[i]=i*i;
}
for(i=0; i<rows; ++i) {//第三次打印非法(但大部分情况下会打印出非法修改后的数据)
cout<<p[i]<<"/t";
}
cout<<endl;
}
这个问题的关键是delete释放的空间是指针指向的动态空间,而指针本身依然保存着它本来保存的地址,只是这个地址对应的空间已经释放了。如果再通过这个指针访问这个地址那就构成非法,因为这些空间可能已被其它程序使用。
因此比较好一点的书(比如C++ Primer)上会这么说,在delete空间后最好把指针置为0,像这样:delete[] p; p=0;。这样做的好处是如果你还犯上面那样的错误,程序就会报一个运行时错误来提醒你本程序有错你应该修改而不会像原来那样以为程序是正确的。