浅谈malloc-free and new-delete

浅谈malloc-free and new-delete

        对于每一个C++程序员来说,malloc/free和new/delete并不陌生,但是又有多少人能够真正理解了呢,我一直就不大理解,直到写这篇博文我仍然处于一知半解的状况,但总算解决了一些疑问。所以仅以此文记录下来,以解后来者之惑,同时供以后自己参考学习之用,水平有限,欢迎拍砖。

 

一.  问题提出

(1)   malloc/free和 new/delete有何区别?

(2)   为何malloc只需传递一个size形参,却不需要内存数据类型?

(3)   为何free不需要传递size形参,而只需传递形参,且free形参类      型为void*那free是如何知道普通数据及数组大小的。

(4)  为何new/delete对普通数据和对数组的处理不同,数组为何加[]符号?且delete释放数组时也不需要对象个数?

 

 

二.   问题解释

(1)       malloc/free 与 new/delete区别

区别在于malloc/free是C标准库函数,处理的是内置类型及结构体等数据的分配释放工作。而new/delete是C++内置操作符,它们不仅负责内置类型的分配释放,还处理对象类型的自定义类型的数据的分配和释放工作。它在对象分配时会调用构造函数,释放时会调用析构函数。很多实现中后者是对前者的封装,并且加入了类类型的处理机制,它只不过是比malloc/free做的工作多些罢了。

(2)        malloc只需要传递一个size_t类型的参数,是因为malloc分配内存时并不关心数据类型,它是以字节为单位分配空间,把首地址作为void*返回,转换工作交给程序员来完成。其实在<<C和指针>>这本书中我们就已经充分体会void*的强大之处,<<STL源码剖析>>一书中也有体现。

(3)       而free不需要表示长度的型参似乎有点诡异,由于free接受的是void*类型的指针,所以它也没有可能以类型来获取长度,特别是如果free的目标是数组的话。要解释这个问题就不得不说明一个问题,那就是事实上malloc在分配内存是不仅仅分配所需空间大小,同时还会分配一些额外的空间以记录内存分配信息:

根据inside The C++ Object  Model上说:现代C++编译器执行malloc函数时会记录分配空间的长度信息,实现机制一般有两种:

1)  cookies:将一个记录内存分配大小的小块綁定在分配内存的地址头部(有些可能是尾部)

如:

int  arr[10];

n  arr[0] arr[1]  arr[2]  arr[3] ......

n表示字节数。

2)  空间分配表:系统会维护一个专门的内存分配表,在分配表中记录分配的内存的大小信息。

这里给出网上找到的一个vs2010作为测试环境,(猜测应该是win7 32位系统)下得出的内存分配公式:

对于普通数据:

公式():_CrtMemBlockHeader +<your data>+gap[N]

对于对象数组:

公式():_CrtMemBlockHeader +数组元素个数+<yourdata>+gap[N]

其中_CrtMemBlockHeader是用于存储内存分配信息的数据结构,其定义可能不同系统不同,一个例子为:

typedefstruct _CrtMemBlockHeader

{     

// 指向前一块数据块的指针     

  struct _CrtMemBlockHeader *pBlockHeaderNext;     

// 指向下一块数据块的指针     

    struct _CrtMemBlockHeader *pBlockHeaderPrev;   

 

    // File name请求内存分配操作的那行代码所在的文件的路径和名称但实际上是空指针 

    char *szFileName; 

    // Line number行号请求内存分配操作的那行代码的行号  

    int nLine;           

 

    // 请求分配的大小
    size_t nDataSize;    

 

    // Type of block类型 

    int nBlockUse;     

    // 请求号

    long lRequest;       

    // 这个数据是干嘛的呢,查下单词gap是什么意思,你就知道了!

    unsigned char gap[nNoMansLandSize];

} _CrtMemBlockHeader;

gap[]是越界符,如0xFD。

至此我们解释了该问题,以此类推delete不需要大小的参数其原理大抵如此。

(4)       这个问题其实设计下面这个结论:

1)  对于单个数据必须使用delete

2)  对于普通数组(这些数组包括只含合成的析构函数的类数组-----trival deconstructor)用delete和delete[]都可以,是等价的。

3)  对于显示定义了析构函数的类数组必须使用delete[]

那么为什么会这样呢,那是因为对于有显示析构函数的类数组构造时其存储方式是公式()当你使delete时,它会把它当普通数据处理,那么它会把:

_CrtMemBlockHeader+数组元素个数

这9个字节的后8个字节当作_CrtMemBlockHeader来处理,那么读出的内存长度必然是错的,这样释放空间通过越界检查gap[]就可以发现内存释放操作错误,结果报错。

 

 

对于这个话题先讨论到这,进一步的研究等看完后续章节。

下面摘抄网上一段文章,我觉得能解释一些问题:

但因为new-delete申请和释放是类型相关的,所以其内部不仅关联到所申请内存的大小,还涉及到构造的类对象的个数。如代码new TYPE[num]。至于内存的大小我们已经明晰了,但是类对象的个数是否要记录呢,如果要记录,那存在哪里呢?可能现在有人会说不用记录啊,知道类对象的类型直接sizeof(TYPE),然后再用内存大小去除,不就可以了?答案是否定的。因为你申请的内存并不等于类对象的个数与类对象大小的乘积,它还存储额外的信息,如header,而且还要考虑到内存申请时的字节对齐。soso,无法通过内存大小计算,所以该类对象个数num是要记录的。也就是说new数组要记录两个数,这点是很关键的。那类对象个数num是记录在哪呢?是否也记录在内存里?no,不是,类对象的个数是有编译器编译过程中存储记录的(参考侯杰大师翻译的《effective c++》),而不是通过内存管理器。当你delete时,他会自动找打该指针对应的类对象的个数,循环调用析构函数,然后再free这块内存。

 

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