您的位置:首页 > 其它

为什么new/delete和new[]/delete[]必须配对使用?

2018-03-23 16:32 387 查看
new和delete的内部机制这里不赘述了,戳这里《浅谈 C++ 中的 new/delete 和 new[]/delete[]》 
glibc的mallc和free实现的内存分配释放简介,戳这里《malloc和free的内存到底有多大?——GNU glib库》 
第一篇博客讲的很好,但是最后new、delete的为什么配对使用的解释单纯理解还不到位。这里总结并补充说明一下。动态内存使用表
分配方式删除方式结果结果分析
newdelete成功合法
newdelete[]失败参考下文Q1
new[]delete内嵌类型成功;自定义类型失败参考下文Q2
new[]delete[]成功合法

合法的new/delete和mallc/free示意图


 
说明:如果对象按照字节对齐的话,那么对象之间可能存在填充,因此图中的对象间隔不一定存在。前提知识1:第一篇博客中已经阐明:new [] 时多分配 4 个字节,用于存储用户实际分配的对象个数。而new不会多分配。 
前提知识2:在第二篇博客中,介绍了mallc和free的实际大小,这个信息存储在内存块的块头里面。其中最重要的就是指示实际分配的内存大小(单位:字节),那么在free时,就要将用户的传入的地址,减去块头长度找到实际分配内存的起始地址然后释放掉块头的长度是8字节。 
知道这两个前提知识下面两个问题就好解释了。

Q1: new为什么使用delete[]失败?

new时不会偏移4字节,delete[]时,编译器就是通过标识符[]而执行减4字节操作。从上图可知,减后的地址值会落到块头中,同时编译器从块头中提取4字节中的值作为自己执行析构对象的个数,而这4个字节是实际内存的长度,这是一个比较偏大的值,比如256,然后编译器对随后的内存对象执行析构,基本上都会导致内存越界,这必然失败。

Q2:new[]为什么内嵌类型使用delete成功,自定义类型delete失败?

new[],如果是内嵌类型,比如char或者int等等,就是C数组,那么它不会向后(地址变大的方向)偏移4个字节。因此执行delete时,显然不会出现任何问题。 
但是如果是自定义类型呢?那么new[]时就会向后偏移4个字节,从malloc的返回地址偏移4个字节用来存储对象个数,如果使用delete,编译器是识别不出释放的是数组,那么它会直接将传入对象的首地址值处执行一次对象析构,这个时候还不会出现问题,但是再进一步,它把对象的首地址值传递给free时,那么这个地址值并不是malloc返回的地址,而是相差了4个字节,此时free向前偏移取出malloc的实际长度(//当成块头)时,就会取出对象的个数值作为实际的分配长度进行释放,显然这将导致只释放了n字节,其余的块头一部分和除n字节的所有内存都泄露了,并且只有第一个对象成功析构,其余都没有析构操作。一般对象个数n是个非常小的值,比如128个对象,那么free只释放了128字节。(注意:不同的libc实现不同,这里只示例阐述原理,不深究数字) 


好了,解释清楚了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: