使用delete释放new[]的空间造成的错误分析

曲折探索后,这个问题算是水落石出。

我们都被告诫,new和delete,new[]和delete[]要成对出现。如果使用delete 释放new[] 申请的空间会发什么?如下:

T* p = new T [1024];

....//do something

delete p;//会发生什么?

我先告诉你,如果T是一个base type(如,int、double、char),你会发现,程序依然正确运行,不仅如此,内存空间也被正确释放。它的正确运行,让很多人对delete[]和delete的区别的探索就此终止。也难怪在国内的博客中分析delete和delete[]的原理和delete 释放new[]的文章少之又少,很多相关的文章关于这个问题一句带过。

如果代码是这样的:

class A
{
    public:
    int m;
    ~A()
    {}
};

int main()
{
    A* ptr = new A[1024];
    delete ptr;
    return 0;
 }

你可以测试一下这段代码,在VS和g++下都测试一下,看看都出现什么结果。这会增加你日后在程序调试bug的经验(我已经遭遇一次)


要明白这个问题,我们要知道new和new[],delete和delete[]都干什么了些什么。

不用多说new和delete。

1.new封装了C语言中的malloc 函数,申请一块内存空间,如果T是用户自定义的类类型,将使用T的构造函数初始化这块内存空间上的对象。

2.delete封装了C语言中的free 函数,如果T是用户自定义的类类型,首先调用T的析构函数,再调用free释放这个对象占用的内存空间。

new[]会更复杂一些:

T* ptr = new  T [1024];
delete[] ptr;

我们注意到,没有传入任何关于ptr指针的信息给delete[]操作符运算符(operator)。最初的C++编译器版本是要求程序员传入关于指针指向空间的大小的参数,而现代的编译器虽然也接受,但多数情况会忽视它[1]!

既然没有传入参数,那编译器一定是通过某种手段获知ptr指向空间的大小。

是的,说某种手段,是因为有多种方法。在C++发展的最初,有一种关联数组的方法:T* ptr = new[s]申请一段空间后,编译器会在内存的某个地方固定的数组中存入ptr和ptr对应的空间大小s,下次在调用delete[]的时候,就会查找这个数组中的ptr对应的s。[2]

这个方法,现代的编译器已经很少见了,因为它很慢。

现代的g++、vc、icpc都使用信息头(additional information head block)的方法来管理。在new[]申请内存空间时,会多申请n个字节,这n个字节就是信息头,位于我们ptr指针向低地址偏移n个字节的地方(block = ptr - n)。每次delete[]被调用时,编译器就会去这个信息头查找申请的内存信息。

所以,调用new[]得到的东西和调用malloc得到的东西并不相同!

现在,我们分析一下

A* ptr = new A [1024];

delete ptr;

这段代码在delete的时候发生了什么:

首先,和delete任何时候一样,编译器会给delete指派A的析构函数,以函数指针的形式,delete会调用这个析构函数,作用于ptr指向的这个对象上,显然ptr之后的1023个对象不会调用析构函数,因为我们使用的是delete而不是delete[]。然后,delete执行free函数,释放申请的空间。

但是!我们new[]中调用malloc得到的指针并不是从ptr开始的,我们的程序找不到malloc分配的ptr指针,就会崩溃![3]

而为什么base type不会有问题呢?程序运行得好好的。是的,都是编译器的原因。编译器检查到,如果是base tyep,new[]操作就会省去前面的信息头,后面发生delete释放new[]的时候,free调用的ptr和malloc调用的ptr也就一致了。当然,这不一定,也有可能某些编译器,在某些时候,即便是base type它也要加上head block呢?!

所以,如果new和delete,new[]和delete[]不配对,发生什么,未定义!

看到这里,相信你已经明白了delete[]为什么要对应new[]了,而delete也不能和delete[]混用的原因。

有什么问题欢迎留言,交流。


[1][2]《深入探索C++对象模型》

[3]http://stackoverflow.com/questions/8940090/is-it-safe-on-linux-to-mix-new-and-delete    ”LiKao“ 的回答

原文地址:https://www.cnblogs.com/wangpei0522/p/4476470.html