zoukankan      html  css  js  c++  java
  • 使用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“ 的回答

  • 相关阅读:
    pycharm下载与安装
    IOS 字体表
    iOS 在 ARC 环境下 dealloc 的使用、理解误区
    微信支付的开发
    Undefined symbols for architecture i386:和"_OBJC_CLASS_$_xx", referenced from:问题解决方法
    soureTree使用
    UINavigationController 直接pop到指定controllerView的方法
    OBC: NSString 与 NSArray 互转
    NSArray的containsObject的使用时注意的问题
    IOS 自定义导航栏标题和返回按钮标题
  • 原文地址:https://www.cnblogs.com/wangpei0522/p/4476470.html
Copyright © 2011-2022 走看看