C++内存管理之一(检测内存泄露)

Unity游戏Mod开发指南:MelonLoader加载器原理与实战 在Unity游戏生态中,Mod加载器是实现游戏内容扩展的核心技术组件。其工作原理基于程序集注入与托管机制,通过在游戏启动时拦截程序集加载流程,实现自定义代码的动态加载与执行。这项技术的核心价值在于为游戏开发者与爱好者提供了标准化的二次开发框架,将原本零散的修改行为转化为可维护、可管理的模块化扩展。在应用场景上,它广泛应用于《幻兽帕鲁》、《饥荒》、《星露谷物语》等热门Unity游戏的模组开发中,显著提升了Mod的兼容性与开发效率。MelonLoader作为当前主流的Unity Mod加载器,通过分层架构设计实 阅读详情

  C++程序的复杂性很大一部分在于他的内存管理,没有C#那样的垃圾回收机制,内存管理对初学者来说很困难。经常会出现内存泄露的情况。那么我们写程序如 何避免内存泄露呢?首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复。
 本文描述了如何检测内存泄露。最主要的是纯C,C++的程序如何检测内存泄露。
 现 在有很多专业的检测工具,比如比较有名的BoundsCheck, 但是这类工具也有他的缺点,我认为首先BoundsCheck是商业软件,呵呵。然后呢需要安装,使用起来不太方便。因为我们检测的时候不一定经常会启动 他来检测。这样经常会积累很多问题,那时要解决就麻烦了。最好就是从开始编码,一步一步的都能随时提醒我们内存泄露。我们编程序会经常调试,假如能在每次 调试程序的时候都能自动检测内存泄露就好了。

  一. 在 MFC 中检测内存泄漏
假如是用MFC的程序的话,很简单。默认的就有内存泄露检测的功能。
我们用VS2005生成了一个MFC的对话框的程序,发现他可以自动的检测内存泄露.不用我们做任何特殊的操作. 仔细观察,发现在每个CPP文件中,都有下面的代码:

  1. #ifdef _DEBUG
  2. #define new DEBUG_NEW
  3. #endif

DEBUG_NEW 这个宏定义在afx.h文件中,就是它帮助我们定位内存泄漏。

    在含有以上代码的cpp文件中分配内存后假如没有删除,那么停止程序的时候,VisualStudio的Output窗口就会显示如下的信息了:

  1. Detected memory leaks!
  2. Dumping objects ->
  3. d:/ code /mfctest/mfctest.cpp( 80 ) : { 157 }  normal   block  at  0x003 AF 170 ,  4  bytes long.
  4.  Data: < >  00 00   00 00  
  5. Object dump complete.

    在Output窗口双击粗体字那一行,那么IDE就会打开该文件,定位到该行,很容易看出是哪出现了内存泄露。

    二.检测纯C++的程序内存泄露

我试了下用VisualStudio建立的Win32 Console Application和Win32 Project项目,结果都不能检测出内存泄露。
下面一步一步来把程序的内存泄露检测的机制建立起来。
首先,我们需要知道C运行库的Debug版本提供了许多检测功能,使得我们更容易的Debug程序。在MSDN中有专门的章节讲这个,叫做Debug Routines,建议大家先看看里面的内容吧。
我们会用到里面很重要的几个函数。其中最重要的是 _CrtDumpMemoryLeaks();自己看MSDN里的帮助吧。使用这个函数,需要包含头文件crtdbg.h
该函数只在Debug版本才有用,当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在“Output(输出)”窗口中显示内存泄漏信息.写段代码试验一下吧,如下:
  检测内存泄露版本一 :

  1. #include "stdafx.h"
  2. #include <crtdbg.h>
  3. int  _tmain( int  argc, _TCHAR* argv[])
  4. {
  5.      int * p =  new   int ();
  6.     _CrtDumpMemoryLeaks();
  7.      return  0;
  8. }

  运行后,在Output(输出)窗口,显示了如下的信息:

  1. Detected memory leaks!
  2. Dumping objects ->
  3. {112} normal block at 0x003AA770, 4 bytes  long .
  4.  Data: <    > 00 00 00 00 
  5. Object dump complete.

  但是这个只是告诉我们程序有内存泄露,到底在哪泄露了一眼看不出来啊。
  看我们的检测内存泄露版本二 :

  1. #include "stdafx.h"
  2. #ifdef _DEBUG
  3. #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
  4. #else
  5. #define DEBUG_CLIENTBLOCK
  6. #endif
  7. #define _CRTDBG_MAP_ALLOC
  8. #include <crtdbg.h>
  9. #ifdef _DEBUG
  10. #define new DEBUG_CLIENTBLOCK
  11. #endif
  12. int  _tmain( int  argc, _TCHAR* argv[])
  13. {
  14.      int * p =  new   int ();
  15.     _CrtDumpMemoryLeaks();
  16.      return  0;
  17. }

  该程序定义了几个宏,通过宏将Debug版本下的new给替换了,新的new记录下了调用new时的文件名和代码行.运行后,可以看到如下的结果:

  1. Detected memory leaks!
  2. Dumping objects ->
  3. d:/code/consoletest/consoletest.cpp(21) : {112} client block at 0x003A38B0, subtype 0, 4 bytes  long .
  4.  Data: <    > 00 00 00 00 
  5. Object dump complete.

  呵呵,已经和MFC程序的效果一样了,但是等一等。看下如下的代码吧:

  1. int  _tmain( int  argc, _TCHAR* argv[])
  2. {
  3.      int * p =  new   int ();
  4.     _CrtDumpMemoryLeaks();
  5.      delete  p;
  6.      return  0;
  7. }

  运行后可以发现我们删除了指针,但是它仍然报内存泄露。所以可以想象,每调用一次new,程序内部都会将该调用记录下来,类似于有个数组记录,假如 delete了,那么就将其从数组中删除,而_CrtDumpMemoryLeaks()就是把这个数组当前的状态打印出来。
所以除了在必要的时候Dump出内存信息外,最重要的就是在程序退出的时候需要掉用一次_CrtDumpMemoryLeaks();
 假如程序有不止一个出口,那么我们就需要在多个地方都调用该函数。
 更进一步,假如程序在类的析构函数里删除指针,怎么办?例如:

  1. #include "stdafx.h"
  2. #ifdef _DEBUG
  3. #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
  4. #else
  5. #define DEBUG_CLIENTBLOCK
  6. #endif
  7. #define _CRTDBG_MAP_ALLOC
  8. #include <crtdbg.h>
  9. #ifdef _DEBUG
  10. #define new DEBUG_CLIENTBLOCK
  11. #endif
  12. class  Test
  13. {
  14. public :
  15.     Test()      {   _p =  new   int ();     }
  16.     ~Test()     {    delete  _p;          }
  17.      int * _p;
  18. };
  19. int  _tmain( int  argc, _TCHAR* argv[])
  20. {
  21.      int * p =  new   int ();
  22.      delete  p;
  23.     Test t;
  24.     _CrtDumpMemoryLeaks();
  25.      return  0;
  26. }

  可以看到析构函数在程序退出的时候才调用,明明没有内存泄露,但是这样的写法还是报了。
  如何改进呢,看检测内存泄露版本三 :

  1. #include "stdafx.h"
  2. #ifdef _DEBUG
  3. #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
  4. #else
  5. #define DEBUG_CLIENTBLOCK
  6. #endif
  7. #define _CRTDBG_MAP_ALLOC
  8. #include <crtdbg.h>
  9. #ifdef _DEBUG
  10. #define new DEBUG_CLIENTBLOCK
  11. #endif
  12. class  Test
  13. {
  14. public :
  15.     Test()      {   _p =  new   int ();     }
  16.     ~Test()     {    delete  _p;          }
  17.      int * _p;
  18. };
  19. int  _tmain( int  argc, _TCHAR* argv[])
  20. {
  21.     _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
  22.      int * p =  new   int ();
  23.      delete  p;
  24.     Test t;
  25.      return  0;
  26. }

  _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。必须同时设置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF.
  这样,该版本已经达到了MFC一样的效果了,但是我觉得光这样还不够,因为我们只是在Output窗口中输出信息,对开发人员的提醒还不明显,经常会被遗 漏,而且很多人就算发现了内存泄露,但是不好修复,不会严重影响到程序外在表现,都不会修复。怎么样能让开发人员主动的修复内存泄露的问题呢?记得曾经和 人配合写程序,我的函数参数有要求,不能为空,但是别人老是传空值,没办法了,只好在函数开始验证函数参数,给他assert住,这样程序运行时老是不停 的弹出assert,调试程序那个烦压,最后其他程序员烦了,就把这个问题给改好了,输入参数就正确了。所以我觉得咱要让程序员主动去做一件事,首先要让 他觉得做这个事是能减轻自己负担,让自己工作轻松的。呵呵,那咱们也这样,当程序退出时,检测到内存泄露就让程序提示出来。
  看检测内存泄露版本四 :

  1. #include "stdafx.h"
  2. #include <assert.h>
  3. #ifdef _DEBUG
  4. #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
  5. #else
  6. #define DEBUG_CLIENTBLOCK
  7. #endif
  8. #define _CRTDBG_MAP_ALLOC
  9. #include <crtdbg.h>
  10. #ifdef _DEBUG
  11. #define new DEBUG_CLIENTBLOCK
  12. #endif
  13. void  Exit()
  14. {
  15.      int  i = _CrtDumpMemoryLeaks();
  16.     assert( i == 0);
  17. }
  18. int  _tmain( int  argc, _TCHAR* argv[])
  19. {
  20.     atexit(Exit);
  21.      int * p =  new   int ();
  22.      return  0;
  23. }

  该版本会在程序退出时检查内存泄露,假如存在就会弹出提示对话框.
 atexit(Exit);设置了在程序退出时执行Exit()函数。
 Exit()函数中,假如存在内存泄露,_CrtDumpMemoryLeaks()会返回非0值,就会被assert住了。

   到这个版本已经达到可以使用的程度了。但是我们还可以做些改进,因为真要准确的检测到代码中所有的内存泄露,需要把代码中的#define……拷贝到所 有使用new的文件中。不可能每个文件都拷贝这么多代码,所以我们可以将他提取出来,放在一个文件中,比如我是放在 KDetectMemoryLeak.h中,该文件内容如下:

  1. #pragma once
  2. #ifdef _DEBUG
  3. #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
  4. #else
  5. #define DEBUG_CLIENTBLOCK
  6. #endif
  7. #define _CRTDBG_MAP_ALLOC
  8. #include <stdlib.h>
  9. #include <crtdbg.h>
  10. #ifdef _DEBUG
  11. #define new DEBUG_CLIENTBLOCK
  12. #endif

  然后将KDetectMemoryLeak.h包含在项目的通用文件中,例如用VS建的项目就将其包含在stdafx.h中。或者我自己建的一个Common.h文件中,该文件包含一些通用的,基本所有文件都会用到的代码东东。

  好了,到现在,检测内存泄露总算完成了,而且他还能定位到到底是代码中哪个文件,哪行出现了内存泄露。下一篇文章将会讲些实际遇到的一些问题,例如只知道 有内存泄露,但是不知道到底内存泄露的具体位置,如何利用内存断点等技术来定位内存泄露的位置啊,最后会从代码的角度讲下,怎么样才能避免内存泄露吧。





 

本文来自
http://blog.youkuaiyun.com/zxcred

c++内存检测原理 Memcheck 能够检测出内存问题, 关键在于其建立了两个全局表: •Valid-Value 表: 对于进程的整个地址空间中的每一个字节(byte),都有与之对应的 8 个 bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。 •Valid-Address 表:对于进程整个地址空间中的每一个字节(byte),还有与之对应的 1 个 bit,负责记录该地址是否能够被读写。 检测原理: •当要读写内存中某个字节时,首先 阅读详情

相关推荐

【并发编程五】c++进程通信——共享内存(shared memmory)

现代操作系统,对于内存管理,采⽤的是虚拟内存技术,也就是每个进程都有⾃⼰独⽴的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。所以,即使进程 A中 和 进程 B中 的虚拟地址是⼀样的,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响。这样这个进程写⼊的东⻄,另外⼀个进程⻢上就能看到了,都不需要拷⻉来拷⻉去,传来传去,⼤⼤提⾼了进程间通信的速度。⽤了共享内存通信⽅式,带来新的问题,那就是如果多个进程同时修改同⼀个共享内存,很有可能就冲突了。那共享内存的⽅式,就很好的解决了这⼀问题。

junxuezheng的博客 1万+

如何避免和检查C++中的内存错误

内存问题一直是C/C++程序员的心头大患,因为没有GC机制,所以需要我们自己管理内存。在 从“new和malloc的不同”出发看CC++的内存分配 一文中,讲述了几种内存错误的例子,那么避免这些陷阱呢? 1. new/delete 、malloc/free、new[]/delete[] 不匹配,导致的内存污染或者内存泄漏 2. 野指针,delete/free之后,没有置NULL,再次使用 3. delete/free之后,再次释放,double free 3. 申请内存之后,没有判断是否成功,直接

不才的专栏 1281

QMT与通达信代码实战:从预警到交易的自动化实现

本文详细介绍了如何利用QMT量化交易平台与通达信软件实现从预警到交易的全自动化。通过读取通达信预警生成的板块文件,QMT策略可自动执行买卖指令,有效解决手动交易滞后问题。文章提供了完整的代码框架、核心原理拆解及实盘注意事项,帮助投资者构建高效、纪律性的自动化交易系统。

ansible6ops的博客 648

【内存管理】C++堆栈内存管理及内存泄露检测方法

C++栈内存和堆内存管理

工业电子技术研读及分享 2234

从理论到板级:FOC电机驱动硬件电路全链路设计解析

本文从硬件工程师视角,深度解析FOC电机驱动从理论到板级实现的全链路设计。重点剖析了电源保护、核心采样电路、三相逆变桥与驱动电路等关键硬件模块的设计要点与实战经验,强调硬件是FOC算法可靠运行的基石,并提供了PCB布局布线的核心原则,帮助开发者构建高性能、高可靠性的电机驱动硬件平台。

weixin_29240711的博客 332

目标检测YOLO实战应用案例100讲-跨模态、多级别融合的RGB-D显著目标检测方法研究(续)

在第二章介绍了本研究的跨模态特征融合方法,第二章对于多级别特征的融合, 使用的是与基线模型baseline相同的方法:相邻级别的特征中,高级特征进行上采 样到与低级特征相同的尺度,再将高级特征通过降维得到与低级特征相同的通道数 量,最后对二者进行特征叠加,如此自顶向下的进行多级别的特征融合。本章主要 介绍本研究在特征层面的另一发现,并基于此发现引出的本文的第二个创新点:多 级别特征的融合方法。另外,在网络最深层,本文设计了一个新的模块来获取大的 感受野,对于大尺度目标能够增加更多语义信息,提升检测效果。 骨

qq_36130719的博客 466

C++内存管理之一(检测内存泄露) 转

<br />C++程序的复杂性很大一部分在于他的内存管理,没有C#那样的垃圾回收机制,内存管理对初学者来说很困难。经常会出现内存泄露的情况。那么我们写程序如何避免内存泄露呢?首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复。<br />    本文描述了如何检测内存泄露。最主要的是纯C,C++的程序如何检测内存泄露。<br />现在有很多专业的检测工具,比如比较有名的BoundsCheck, 但是这类工具也有他的缺点,我认为首先BoundsCheck是商业软件,呵呵

werflychen的专栏 4884

YOLOv8改进,YOLOv8添加DCNv3可变性卷积,无需编译

DCNv3在DCNv2版本上进行了改进,上一期我们在YOLOv8中添加了DCNv2,这一期我们将添加DCNv3,DCNv3代码详解可以参考链接:代码详解——可变形卷积(DCNv3)

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 5020

C/C++内存管理详解以及内存泄露的检测

文章目录前言一、C/C++内存分布?内存存放的数据类型二、C/C++内存管理1.C内存管理2.C++内存管理总结 前言 这篇博客主要介绍一些C/C++的内存管理机制,包括C/C++内存分布、申请释放内存方式及底层实现的简单解释、内存泄漏、内存泄漏的检测、如何防止内存泄漏,若有错误之处还请指正。 一、C/C++内存分布? ## 1.内存布局示意图: 内存存放的数据类型 栈:非静态局部变量/函数参数/返回值等等,栈是向下增长的。 内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库..

weixin_45295598的博客 594

PCL RANSAC拟合分割圆柱体模型【2026最新版】

使用RANSAC拟合分割圆柱的原理介绍、官网示例代码实现以及一个可视化拟合出来的圆柱的具体应用案例。博客长期更新,最近一次更新时间为:2026年1月28日。

点云侠的博客 40万+

从源码编译CesiumForUnreal

从源码编译CesiumForUnreal插件

专注GIS开发 5643
上一篇: SkinMagic使用方法总结--SetSingleDialogSkin断言失败
下一篇: C++0x语言新特性一览
study_live
博客等级 码龄19年 20粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值