C++ 重复释放同一块内存空间没有报错的思考

是因为重复使用delete删除相同的内存空间,但编译器竟然没有报错,所以开始了一系列关于内存空间的尝试和思考。

1. 内存空间被释放,不代表存储的内容被删除。

一块内存被delete后,只是从是否可以被再次分配的意义上进行了释放,其中原先存储的内容不会立刻被抹除。可以简单写代码验证如下:

int main()
{
    int *a1 = new int(3);
    cout << "*a1 = " << *a1 << endl;
    delete a1;
    cout << "after delete a1" << endl;
    cout << "*a1 = " << *a1 << endl;
    
    return 0;
}

输出结果为:

*a1 = 3
after delete a1
*a1 = 3

这里使用delete删除了指针a1,这意味着a1指向的内存地址被释放,但是不代表a1这个指针被删除!所以指针a1仍然指向这块内存,而内存中的内容很可能也没有被抹去,所以仍然能打印出上面的结果。一般对这个问题的解释是需要把a1变为空,不然就是野指针了。增加语句a1 = nullptr;某种程度上解决了这个问题,所以通常会要求在delete语句之后,一定要将指针也设置为空指针。规范的写法如下:

int main()
{
    int *a1 = new int(3);
    
    cout << "*a1 = " << *a1 << endl;
    delete a1;
    a1 = nullptr;
    cout << "after delete a1" << endl;
    cout << "*a1 = " << *a1 << endl;

    return 0;
}

输出结果为:

*a1 = 3
after delete a1
*a1 =

2. 重复释放内存空间,有可能通过编译,但一定不要这样做。

为了复现重复释放内存空间的问题的,我有创建了指针b,并将a指针赋值给b指针(指针赋值其实就是改变指针的指向,所以是浅拷贝)。参看下面的代码:

int *a1 = new int(3);
int *b1 = a1;

cout << "*a1 = " << *a1 << endl;
delete a1;
a1 = nullptr;

cout << "*b1 = " << *b1 << endl;
delete b1; // 释放了两次相同的内存空间
b1 = nullptr;

在这里,a指针和b指针实际上都指向同样的内存空间(输出结果中a1=b1,也印证了指针是浅拷贝这件事),所以通过a1释放了其指向的内存空间后,意味着b1指向的相同的内存空间也是被释放的。我再delete b1就相当于重复释放,但是编译器竟然通过并执行了,输出结果如下:

*a1 = 3
a1 = 0x2781c80
*b1 = 3
b1 = 0x2781c80

我在网上找了很多资料,不少人重复释放空间后是无法正常执行的,我猜可能和编译器有关,但千万别认为这样写是可以的。实际上我纠结这个问题有点掉书袋,一直以为重复释放本身就应该被限制。实际上问题不出在重复释放上,问题出在一个内存空间被释放了,就不应该有任何的指针或者变量能够访问这个空间,不止再次释放这个操作不能被进行,其他操作如写入等也不应该不进行。因为一旦一个内存空间被释放后,如果后续程序没有结束,继续申请内存,很可能申请到之前释放了的内存。那么,这时候如果通过之前没有清除干净的指针进行了删除等的操作,就会对现在本来有用的内存造成影响。可以尝试如下的代码,但很可能做不出那种效果,但对理解这个问题很有帮助:

int *a1 = new int(3);
cout << "*a1 = " << *a1 << endl;
cout << "a1 = " << a1 << endl;
delete a1;
   
int *c1 = new int(5);
cout << "c1 = " << c1 << endl;
*a1 = 4;
cout << "*c1 = " << *c1 << endl;
cout << "*a1 = " << *a1 << endl; 

我的输出结果是:

*a1 = 3
a1 = 0x6e7188
c1 = 0x6e7128
*c1 = 5
*a1 = 4

网上有人能够做出c1的值被改为和a1中的值相同的结果。我之前试出来过一次,但很难复现。其实就是程序恰好把释放了的a1指向的空间又立刻给了申请新空间的c1。虽然比较难做出来,但是理论上是可能的。那么就会出现通过已经删除了的指针指向新的有用的内存空间的情况。所以,结论是一旦我们释放了一个内存空间,必须保证不再通过任何其他残留的指针或变量能够访问到它。而上面那段代码的问题仍然是,在delete a1之后,应该将指针a1置空,这样后面再给a1赋值的操作就无法进行了。

你可能感兴趣的:(C++)