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

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

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实现不同,这里只示例阐述原理,不深究数字)
这里写图片描述

好了,解释清楚了。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值