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

举报
鱼酱 发表于 2022/01/06 22:11:51 2022/01/06
【摘要】 我们都被告诫,new和delete,new[]和delete[]要成对出现。如果使用delete释放new[] 申请的空间会发什么?如下: T* p = new T [1024]; ....//do something delete p;//会发生什么? 我先告诉你,如果T是一个base type(如,int、double、ch...

我们都被告诫,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;

    return0;

 }

你可以测试一下这段代码,在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. #include "stdafx.h"
  2. int _tmain(int argc, _TCHAR* argv[])
  3. {
  4. int* p = new int ; //size大小 4
  5. *p = 123456789;
  6. int* p2 = new int[1024] ; //size大小 1024*4 = 4096
  7. while(1)
  8. {
  9. }
  10. return 0;
  11. }


 

文章来源: yujiang.blog.csdn.net,作者:鱼酱2333,版权归原作者所有,如需转载,请联系作者。

原文链接:yujiang.blog.csdn.net/article/details/54602705

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。