new出的对象数组必须要用delete[]删除,而普通数组和结构数组delete和delete[]都一样

2023-11-15

为何new出的对象数组必须要用delete[]删除,而普通数组delete和delete[]都一样-----_CrtMemBlockHeader                                                                  

温馨提示:

该文所有测试没有特殊说明都是在Debug模式下!用的是VS2010编译器!

====================================================================================================================================
 
1.在释放堆栈中c++基本数据(包括int,char.....结构体等)的存储空间时,不管是否是数组用delete都不会有错!而且能正常释放所有内存,不会导致内存泄露! 
//程序A  
struct text_data_t  
{  
    int i;  
};  
int _tmain(int argc, _TCHAR* argv[])  
{  
    text_data_t *pdata=new text_data_t[5];  
    char *pi=newchar[5];  
    for(int k=0;k<5;k++)
        pdata[k].i=k;  
    delete pdata;  
    delete pi;  
    //内存泄露检测函数。若检测到内存泄漏那么就会输出宽里输出?"Detected memory leaks!....等信息"  
    _CrtDumpMemoryLeaks();  
}  
没有检测到内存泄露,于是乎,可以看出1是正确的!
 
2)对象数组不能用delete,只能用delete[];
         首先我们需要知道:系统在释放对象数组时,会先执行数组内所有元素的析构函数,然后再调用void operator delete(void *pUserData),一次性将所有分配的数据空间释放!
// 程序B  
class CTextClassA  
{
public:
    int m_num;
    CTextClassA()    {    m_num=0;    };
    ~CTextClassA()    {  cout<<"~CTextClassA()"<<endl;    }  

    void SetNum(int n)    {   m_num=n;   }  
};  
int _tmain(int argc, _TCHAR* argv[])  
{  
    CTextClassA *pa=new CTextClassA;  
    CTextClassA *pas=new CTextClassA[5];  
    CTextClassA *pas_arr[5];  
    for(int i=0;i<5;i++)  
    {  
        pas[i].SetNum(i);  
        pas_arr[i]=&pas[i];  
        cout<<"pas"<<i<<":"<<pas[i].m_num<<"\t";  
    }  
    delete pa;  
    delete pas;  
}  

 

输出结果
调试运行到delete pas;出现保护错!

在release下运行,没有出现上面那个错误提示窗口!但是输出结果是一样的!数组里5个对象只有第一个对象,运行了析构函数!事实证明2的断言同样也是正确的!OK!

 

====================================================================================================================================

那么我就要问了,

           delete 结构体数组----都不会出问题!而delete 对象数组----报错。为什么呢???

如果你深深的被这个疑问所困恼,那么接下来让我们一起来解放这个疑惑!这个痛苦!

 

有些人有这样的误解:

我在网上看了很多帖子,很多人说,程序B:delete  pas;只释放了pas[0]其他的都没有释放;因为根据程序运行结果,我们可以看出,他只调用pas[0]的虚构函数!那么你怎么看呢?你觉得呢?

有人认为可以如下来释放数组所有空间:

//程序C:  
CTextClassA *pas=new CTextClassA[5];  
for(int i=0;i<5;i++)  
{  
    delete pas[i];  
}  

 

那么你,怎么看待程序C;你觉得这样子可以吗?答案你自己去需找!看看运行结果你就会知道!异或是看完全文,那么你也会明白!

 

Ok!debug模式运行程序B,弹出上面错误提示框!按下重试,进入出错函数里

void operator delete(void *pUserData)  
{
    _CrtMemBlockHeader * pHead;
    TCCALLBACK(_RTC_Free_hook, (pUserData, 0));

    if (pUserData == NULL)
        return;

    /* block other threads */
    _mlock(_HEAP_LOCK);    
        __TRY

    /* get a pointer to memory block header */
    pHead = pHdr(pUserData);

    /* verify block type */
    _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));    //程序中断处,按F11进入不了函数内部!那怎么办呢,从数据pHead入手 
    
    _free_dbg( pUserData, pHead->nBlockUse );
    __FINALLY
    _munlock(_HEAP_LOCK);  /* release other threads */
    __END_TRY_FINALLY
        
    return;  
}  

 

Google找到pHead 的类型CrtMemBlockHeader数据结构的解释 参考资料
2) http://hi.baidu.com/6908270270/blog/item/46b854248e0992358644f928.html#0
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[]吧:

<your data>前后各有4个字节的 gap[],前后的gap都为 0xFD.

如果你在自己的Data里写, 不小心越了界(前面或者后面), 系统在delete的时候通过检查 gap 的数据是否还为0xFD,就知道你有没有越界.

当然了, 如果你恰好写的都是0xFD, 那就没法知道了.

函数_CrtDumpMemoryLeaks();就是通过检查分配链表(pBlockHeaderNextpBlockHeaderPrev为双链表的双链), 来查找是否有泄漏。

====================================================================================================================================

我搜索了很多量资料,做了很多实验,得出结论:

对于普通数据存储空间的分配形式:

         公式1)_CrtMemBlockHeader + <Your Data> +gap[nNoMansLandSize];这类数据用deletedelete[]都一样!

通常我们的指针都是指向<your data>的首地址!

而对于对象数组则是:

         公式2)_CrtMemBlockHeader +数组元素个数+ <Your Data> +gap[nNoMansLandSize];

 

举个例子说:

int *pis=new int[5];

当我们的程序执行到这么一条语句时,你觉得系统会给他分配多少内存空间,20?如果你的答案是20那么我可以告诉你,亲,你太单纯了,想得太简单了!那么请再仔细理解前面两个公式!

实际上系统分配sizeof(CrtMemBlockHeader)+20+sizeof(gap[nNoMansLandSize])大小的空间!

而CTextClassA*pas=new CTextClassA[5];

则分配sizeof(CrtMemBlockHeader)+4(该空间,用来存储数组中元素数目大小,占用4Byte+20+sizeof(gap[nNoMansLandSize])大小的空间!

 

OK,也许你不相信我得出的这个结论!我早有准备!

 

调试运行如下代码,并打开内存窗口观察pis:

//程序D:  
int *pis=new int[5];  
for(int i=0;i<5;i++)  
{
    pis[i]=i;  
}  
delete[] pis;

 

内存窗口有关pis内存数据的内容:

从上图数据可以看出

pis[0]在内存里的存储数据为00 00 00 00

pis[1]-----------------------------01 00 00 00(由于我的计算机是Intel,用的是 LittleEndian,所以低位在前高位在后,所以该真正的值为00 00 00 01==1)

pis[2]-----------------------------02 00 00 00(------------------00 00 0002==2)

........

如图可以看出对于,公式(1)是正确的!如果你不信的话可以自己调试下看看!而且证明确实分配的不仅仅只有<your data>!

 

程序B稍加修改,查看pas内存数据

//程序E:  
CTextClassA *pa=new CTextClassA;  
 CTextClassA *pas=new CTextClassA[5];  
 CTextClassA *pas_arr[5];  
for(int i=0;i<5;i++)  
{
    pas[i].SetNum(i);
    pas_arr[i]=&pas[i];  
    cout<<"pas"<<i<<":"<<pas[i].m_num<<"\t"; 
}
delete pa;
delete[] pas;//修改部分  

 

程序E内存数据:

 程序E:进入void operator delete( void *pUserData),查看监视窗口下pHead的值!

事实证明公式2也是正确的!

====================================================================================================================================

OK,由此可证,普通数组和对象的数组存储结构不同,那么会不会就是因为这结构不同导致delete上的不同差异呢?

也就是说是不是,正是因为他这多出来的一个数组元素个数_CrtMemBlockHeader +数组元素个数+ <You Data> + gap[nNoMansLandSize];)导致delete的差异!

 

那么是不是这样呢?究竟是不是介样呢?

好吧,让我们再做个试验来验证下:

在此运行程序B,进入void operatordeletevoid*pUserData)观察内存数据和pHead数据的值:

        pas内存数据和程序E一样,然而pHead的数据可不一样哦!

程序B:;
程序E:;
 
 
大家对比下数据....仔细观察,又没有发现什么倪端?没发现吗?再仔细看看!看出来了吧!哈哈,答案,就在你心中! 先看程序E:pHead的地址刚好比程序B:的地址大4位!这就是症结所在!你观察两份数据也很容易看出:

程序E的pHead->pBlockHeaderPrev==程序B的pHead->pBlockHeaderNext;

程序E的pHead->szFileName==程序B的pHead->pBlockHeaderPrev;

程序E的pHead->nLine==程序B的pHead->szFileName;

程序E的pHead->nDataSize==程序B的pHead->nLine;

……

  再想想四位,四位不刚好是sizeof(数组元素个数)吗?

  恩,不错,确实如此,delete pas;在调用void operato rdelete(void*pUserData)时会将<yourdata>的首地址传给pUserData,

那么程序会将<your data>的前8*4Byte数据当成_CrtMemBlockHeader,也就是说(_CrtMemBlockHeader:: pBlockHeaderPrev开始到  数组元素个数)数据当成CrtMemBlockHeader的数据;

  还记得程序B的中断处吗?_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));该函数是检查,你的数据的数据类型的!而pHead->nBlockUse值已经完全变了,而且变化很大,原本应该是1的可现在是b2!当然要报错了!不报错才怪!然而delete[] pas;则会将 (数组元素个数+<yout data>)整个数据当成pUserData!数组元素个数数据,前8*4Byte当成_CrtMemBlockHeader,写入到pHead!

       恩看到这里相信你明白了,是肿么回事了吧!

       那么回过头来,想想,我们之前的“程序C:”!那么,相信答案就在你心中!

       哈哈……我发现没事可以自己动手实践,这些程序哦!有很多意外收获!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

new出的对象数组必须要用delete[]删除,而普通数组和结构数组delete和delete[]都一样 的相关文章

  • 为 DocumentDb 设置自定义 json 转换器

    我正在使用类型化 DocumentQuery 从 Azure DocumentDb 集合中读取文档 from f in client CreateDocumentQuery
  • 无法在 QGLWidget 中设置所需的 OpenGL 版本

    我正在尝试在 Qt 4 8 2 中使用 QGLWidget 我注意到 QGLWidget 创建的默认上下文不显示 OpenGL 3 1 以上的任何输出 Qt wiki 有一个教程 http qt project org wiki How t
  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • 无法在 CUDA 中找到 1 到 100 数字的简单和?

    我正在研究使用 CUDA 的图像处理算法 在我的算法中 我想使用 CUDA 内核找到图像所有像素的总和 所以我在cuda中制作了内核方法 来测量16位灰度图像的所有像素的总和 但我得到了错误的答案 所以我在cuda中编写了一个简单的程序来查
  • CMake(Ninja 后端)使用 /MT 编译

    我有一个类似的问题CMake 使用 MT 而不是 MD 进行编译 https stackoverflow com questions 14172856 cmake compile with mt instead of md但有一些差异 我正
  • 如何在 C++ 中为指针“this”赋值

    在函数中 如何分配this一个新的价值 您可以分配对象this点于 this XY 但你不能分配直接值this this XY Error Expression is not assignable
  • 如何在 C# 中以编程方式将行添加到 DataGrid?

    正如标题所述 我正在尝试使用 C 以编程方式将行添加到 DataGrid 但我似乎无法使其工作 这是我到目前为止所拥有的 I have a DataGrid declared as dg in the XAML foreach string
  • 如何在 Linux 上重新实现(或包装)系统调用函数?

    假设我想完全接管 open 系统调用 也许要包装实际的系统调用并执行一些日志记录 一种方法是使用 LD PRELOAD http scaryreasoner wordpress com 2007 11 17 using ld preload
  • 从图像创建半透明光标

    是否可以从图像创建光标并使其半透明 我目前正在拍摄自定义图像并覆盖鼠标光标图像 如果我可以将其设为半透明 那就太好了 但不是必需的 销售人员喜欢闪亮的 目前正在做这样的事情 Image cursorImage customImage Get
  • 为什么连续抛出 2 个异常不会生成无法访问的代码警告?

    为什么以下代码行不会创建编译器警告 void Main throw new Exception throw new Exception 据我所知 编译器应该通知您无法到达第二个抛出异常 这显然是一个编译器错误 它是在 C 3 0 中引入的
  • 使用任一默认捕获模式时,这是通过复制捕获还是 (*this) 通过引用捕获?是一样的吗?

    当我看到以下工作时我有点困惑 struct A void g void f g 但后来我发现this https stackoverflow com a 16323119 5825294答案非常详细地解释了它是如何工作的 本质上 它归结为t
  • MINIX内部碎片2

    我正在用 C 语言编写一些软件 它递归地列出给定目录中的所有文件 现在我需要计算出内部碎片 我花了很长时间研究这个问题 发现 ext2 上的内部碎片只发生在最后一个块中 我知道理论上你应该能够从索引节点号获得第一个和最后一个块地址 但我不知
  • fgets溢出后如何清除输入缓冲区?

    当输入字符串超出其预定义限制时 我遇到了 fgets 的小问题 以下面的例子为例 for index 0 index lt max index printf Enter the d string index 1 if fgets input
  • g++ / gcc 是否支持 C++20 新的atomic_flag 功能?

    根据参考参数 https en cppreference com w cpp atomic atomic flag c 20 有丰富的 对我来说有用的 支持atomic flag运营 然而 目前尚不清楚 gcc 是否支持这些功能 它们在任何
  • 异步/等待 - 是*并发*吗?

    我一直在考虑 C 5 中新的异步内容 并且出现了一个特殊问题 据我了解 await关键字是一个简洁的编译器技巧 语法糖来实现连续传递 http en wikipedia org wiki Continuation passing style
  • 跟踪白色背景中的白球(Python/OpenCV)

    我在 Python 3 中使用 OpenCV 来检测白场上的白 黑球 并给出它的精确 x y 半径 和颜色 我使用函数 cv2 Canny 和 cv2 findContours 来找到它 但问题是 cv2 Canny 并不总是检测到圆的完整
  • 尝试后终于没有被调用

    由于某种原因 在我的控制台应用程序中 我无法运行我的finally 块 我编写这段代码是为了测试finally块是如何工作的 所以它非常简单 static void Main int i 0 try int j 1 i Generate a
  • 如何配置 qt Creator 以显示 C++ 代码而不是反汇编程序?

    昨天我做了很多事情 比如更新 GCC Clang 和重新安装 Qt Creator 今天 在逐步调试我的代码时 调试器显示的是反汇编代码 而不是我编写的 C 代码 紧迫F10 or F11 调试器正在进入汇编代码而不是 cpp nor h我
  • 稀疏矩阵超定线性方程组c/c++库

    我需要一个库来解决 Ax b 系统 其中 A 是一个非对称稀疏矩阵 每行有 8 个条目 而且可能很大 我认为实现双共轭梯度的库应该没问题 但我找不到一个有效的库 我尝试过 iml 但 iml sparselib 包中缺少一些标头 有小费吗
  • 将同步 zip 操作转换为异步

    我们有一个现有的库 其中一些方法需要转换为异步方法 但是我不确定如何使用以下方法执行此操作 错误处理已被删除 该方法的目的是压缩文件并将其保存到磁盘 请注意 zip 类不公开任何异步方法 public static bool ZipAndS

随机推荐

  • MyBatis中JdbcType与Oracle、MySql数据类型对应关系详解

    转自 MyBatis中JdbcType与Oracle MySql数据类型对应关系详解 MyBatis 是一款优秀的持久层框架 它支持定制化 SQL 存储过程以及高级映射 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取
  • 我不需要你喜欢我

    绩效考核又开始了 大家心里都在盘算着 老大这次会给我能打多少分呢 大家各有各的心情 有人想得到肯定 也有人在想能不能过关 下面这样的场景一次又一次地在上演 场景1 在主管会议上 部门领导有些不开心地说 你们怎么打的分 A 严重超标啦 怎么没
  • Autosar软件架构

    软件架构 应用层通过 Simulink模型实现 模型的代码生成使用统一配置脚本 底层软件模块满足AUTOSAR 4 2 1标准要求 其软件架构如下图所示 软件架构 2 2 2 Com通信模块配置 BCU通过唤醒信号控制相应CAN消息的通信使
  • Vue.js 最新官方下载地址与项目导入

    目录 VUE2下载网址 VUE2使用示例 VUE3下载与使用 VUE3示例 在官网上下载vue js或者是vue min js 并用
  • shell学习笔记(3)grep -v grep

    网上查阅shell定时脚本相关代码 其中有一句grep v grep awk awk print 2 不是很理解 基础知识太薄弱 pid ps ef grep run jar grep v grep awk print 2 经查阅资料 gr
  • 【零基础 快速学Java】韩顺平 p87-101 四种进制及其相互转换、二进制运算(原码、补码、反码)、7种位运算符

    课程 p87 101 四种进制介绍 小总结 二进制0b开头 八进制0开头 十六进制0x开头 进制字母不区分大小写 直接输出十进制 如 public class temp public static void main String args
  • c++去掉多余空格

    题目 输入一个字符串 字符串中可能包含多个连续的空格 请将多余的空格去掉 只留下一个空格 输入格式 共一行 包含一个字符串 输出格式 输出去掉多余空格后的字符串 占一行 数据范围 输入字符串的长度不超过 200200 保证输入字符串的开头和
  • 什么是价值流图 (Value Stream)?示例汇总

    价值流图 VSM 是一种精益制造技术 用于分析 设计和管理将产品带给客户所需的材料和信息流 它使用标准符号系统来描述各种工作流和信息流 项目被映射为添加值或不从客户的角度添加值 目的是根除不增加价值的项目 值流映射可用于改进可重复步骤的任何
  • 前端这一刻我悟了

    早上没事 在回顾react的时候 突然悟了 发现vue 还是react组件化开发 父组件套子组件 父子组件之间相互通信 写页面样式 逻辑 发送服务器请求 学习主要学的是语法 这些语法写个两三遍就都会了 还真是代码搬运工 再就是代码写的漂亮点
  • vue项目中Echarts图表完整引入、按需加载以及修改主题色

    一 完整引入Echarts 下载安装echarts包 npm install echarts S or yarn add echarts 定义图表显示的容器 并进行渲染
  • 项目资源管理

    目录 申明 1 核心概念 2 虚拟团队 分布式团队 3 规划质量管理 3 1 1 输入 3 1 2 工具和技术 3 1 2 1 责任分配矩阵 3 1 3 输出 4 估算活动资源 4 1 1 输入 4 1 2 工具与技术 4 1 3 输出 4
  • git常用操作指令手册、持续更新....

    一 拉取远程代码到本地流程 1 新建文件夹 git bash here 初始化本地仓库 git init 2 和远程仓库建立连接 git remote add origin 远程地址 3 获取远程分支最新状态 git fetch origi
  • gitee删除远程仓库

    1 登录自己的gitee 2 点击仓库进行选择要删除的仓库 4 进入要删除的仓库 5 在导航栏中点击管理 强调在导航中就有了 6 在左侧的仓库设置中 再点击删除仓库 右侧删除仓库的内容浮现 7 点击删除仓库 点击确认删除 要记得输入gite
  • 【Python 1-7】Python手把手教程之——详解列表List

    列表 作者 弗拉德 来源 弗拉德 公众号 fulade me 列表 在其他语言中又被称为数组 是由一系列按特定顺序排列的元素组成 你可以创建包含字母表中所有字母 数字0 9或所有家庭成员姓名的列表 你也可以创建几个列表 把这几个列表又放在一
  • qvboxlayout布局背景色怎么设置_QT开发(二十一)——QT布局管理器

    一 布局管理器简介 QT中使用绝对定位的布局方式无法自适应窗口的变化 QT中提供了对界面组件进行布局管理的类 用于对界面组件进行管理 能够自动排列窗口中的界面组件 窗口大小变化后自动更新界面组件的大小 QLayout是QT中布局管理器的抽象
  • 西门子200SMART笔记

    第一章 PLC概述 上位机 控件库 HslControls SunnyUI 初级课程 传感器接线方式 棕色 BN 蓝色 BL 黑色 BK 信号线 NPN型 1M M 接 24V PNP型 1M M 接 0V PLC输出接线 电路图 gt 梯
  • 部队脱文档水印软件_网上下载的Word文档有水印?4种方法教你快速去水印!!...

    Hello 各位叨友们好呀 我是叨叨君 我们在网上查找资料下载文档的时候 经常会遇到一些带有水印的文件 那么该怎么把水印去除呢 今天就教大家如何快速去除Word PDF文件中的水印 方法一 设置无水印 打开word文档 点击 设计 工具 再
  • Jquery中$(function(){})

    1 在哪书写js文件 如果我们要执行一段js代码 我们该怎么办呢 1 我们可以写一个js文件 在js文件里写执行函数 然后再 进行引用 2 我们可以直接在HTML页面下 插入脚本 同样是 两种方式没什么区别 唯一的区别就是程序的解耦 所以当
  • 接口测试 —— Requests库GET请求

    Requests库GET请求是使用HTTP协议中的GET请求方式对目标网站发起请求 不带参数的GET请求请看上一篇文章的练习 1 Requests库待参数的GET请求 使用Get方法带参数请求时 是params 参数字典 而不是data 参
  • new出的对象数组必须要用delete[]删除,而普通数组和结构数组delete和delete[]都一样

    为何new出的对象数组必须要用delete 删除 而普通数组delete和delete 都一样 CrtMemBlockHeader 温馨提示 该文所有测试没有特殊说明都是在Debug模式下 用的是VS2010编译器