c++编码中减少内存缺陷的方法和工具

上传人:第*** 文档编号:33595090 上传时间:2018-02-16 格式:DOC 页数:7 大小:38KB
返回 下载 相关 举报
c++编码中减少内存缺陷的方法和工具_第1页
第1页 / 共7页
c++编码中减少内存缺陷的方法和工具_第2页
第2页 / 共7页
c++编码中减少内存缺陷的方法和工具_第3页
第3页 / 共7页
c++编码中减少内存缺陷的方法和工具_第4页
第4页 / 共7页
c++编码中减少内存缺陷的方法和工具_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《c++编码中减少内存缺陷的方法和工具》由会员分享,可在线阅读,更多相关《c++编码中减少内存缺陷的方法和工具(7页珍藏版)》请在金锄头文库上搜索。

1、C+编码中减少内存缺陷的方法和工具内容摘要:基于 C+的软件的缺陷和错误大部分都和内存相关,预防、发现、消除代码中和内存相关的缺陷,成为程序员编写、调试、维护代码时的重要任务。该文基于“面向网络海量空间信息的大型 GIS”课题的工程实践,提出和总结了如何使用 C+语言机制、开发环境和相关质量保证工具来预防、发现各种编译期、运行期内存缺陷的方法和工具。 C+语言是桌面系统,尤其是系统软件、大型应用软件的主流开发语言。C+语言以其灵活性著称,同时也更复杂。利用 C+编写健壮的代码,更具有挑战性。C+允许动态内存管理, 同时也容易导致更多和内存相关的问题。一般而言, 除了系统设计上的缺陷, 基于 C

2、+的软件的缺陷和错误大部分都和内存缺陷(主要包括内存访问错误和内存泄漏两类)相关。 所以,消除代码中的内存相关缺陷,成为程序员编写、调试、维护代码中的任务,也是保证软件质量的关键。本文的工作基于“863”计划项目“面向网络海量空间信息的大型 GIS”课题。该系统是基于 C+/MFC 编写,开发环境是 Visual Studio .net 2003。本文基于此项目的工程实践,总结了如何使用 C+语言机制、开发环境和相关质量保证工具来预防、发现各种编译期、运行期和内存相关的缺陷的方法和工具。1 遵循 C+相关的编码规范和惯用法,预防缺陷编码规范是语言相关的规则,是经过实践总结出来的经验。良好的编程

3、标准将有效地帮助开发人员避免开发有潜在危险的代码。一般来说,为了减少内存缺陷,应该遵循下列编码规则1:(1)基类或者带有虚函数的类应该将其析构函数声明为虚函数。(2)在构造函数中防止内存泄漏,在析构函数中不要抛出异常。(3)使用对应形式的 new 和 delete。即:用 delete 来释放 new 申请的内存,delete释放 new申请的内存。(4)指针在使用前必须初始化,指向动态内存的指针在释放后应立即置为空。(5)如果类构造函数中分配了资源,那么需要显式提供拷贝构造函数和赋值操作符,并且在析构函数中释放资源。值得重视的是 C+中的惯用法 RAII。RAII 核心思想是利用对象来管理资

4、源,在对象的构造函数中获取资源,在其析构函数中释放资源2。为了保证动态申请的内存能在即使出现异常的情况下仍能释放,比较理想的方法是使用局部变量来管理动态内存的所有权(ownership),就是所谓的智能指针。STL 中的 auto_ptr 就是为解决资源所有权问题设计的,但是缺少对引用数和数组的支持并且不能用在 STL 容器中。Boost 库3提供的智能指针相对成熟,实用价值高。其中,shared_ptr 线程安全并且可以用在 STL 容器中。具体示例参考文献3。1.1 编码规范检查工具 CodeWizardCodeWizard 能够对源程序直接进行自动扫描、分析和检查。一旦发现违例,产生信息

5、告知与哪条规则不符并作出解释。以 CodeWizard 4.3 为例,其中内置了超过 500 条编码标准。CodeWizard 可以选择对于当前的工程执行哪些编码标准。CodeWizard 可以和 VC+紧密集成,安装完毕以后,VC+中有 CodeWizard 工具条。1.2 代码检查工具 PC-LintPC-Lint 可检查编译器不易发现的错误。PC-Lint 可对 100 多个 C 库函数进行检查,可以发现标准 C/C+代码中的 1 000 多个常见错误。要把 PC-lint 和 Visual Studio 集成在一起,需要自己配置。Jon Zyzyck 提供了一个报告生成器,可以帮助完成

6、这个工作。可在http:/ 下载。文献4说明了如何在 VC+环境中集成 PC-Lint。2 利用语言机制、开发环境和相关工具以预防和发现内存缺陷发现问题是解决问题的前提。相对于修复内存缺陷,发现内存缺陷并准确定位导致缺陷的代码更为费时费力。及早准确地发现内存缺陷,对于提高开发效率非常重要。2.1 利用断言及早暴露内存缺陷断言是布尔调试语句,用来检测在程序运行的时候某一条件的值是否总为真。断言经常用来确认函数的输入、输出,检查对象的当前状态是否合法等。 在以下的场景使用断言可以帮助发现和内存非法访问相关的错误:(1)验证指针是否可读/写。在函数的入口处,经常需要验证指针所指向的内容区域是否可读/

7、写。 通常采用 assert(p!= NULL)的检测形式。 但是,指针的值不为空并不代表指针指向了合法可读/写内存。Win32 API 提供了函数 IsBadReadPtr、IsBadWritePtr、IsBadStringPtr、IsBadCodePtr 用来检测指针指向的内存区域是否可读/写。C 运行时库提供了_CrtIs ValidPointer、_CrtIsValidHeapPointer 等函数,MFC 库提供了 AfxIsValidAddress、AfxIsValidString 函数来完成类似功能。(2)对基于 MFC 的程序,ASSERT_VALID 宏通过调用重载的 Ass

8、ertValid 函数来确定指向 CObject 派生类对象的指针是否有效。ASSERT_VALID 宏主要调用了 AfxIsValidAddress函数和 CObject 派生类对象的 AssertValid 函数(参考 MFC 源代码 afx.h、objcore.cpp)。2.2 利用 C 运行时刻库检查内存泄漏VC+的 C 运行库(CRT)提供了广泛的功能,帮助用户检测内存泄漏。CRT 提供了_CrtMemCheckPoint、_CrtDump MemoryLeaks、_CrtSetDbgFlag 等函数来帮助调试内存泄漏。对于非 MFC 的工程, 要开启有效的内存泄漏报告功能, 需要进

9、行如下设置:(1)在 StdAfx.h 的头部添加如下代码并开启编译器/Yu 选项:#define _CRTDBG_MAP_ALLOC#include #include #define DEBUG_NEW new(_NORMAL_BLOCK, THIS_FILE, _LINE_)(2)确保在每个.cpp 文件的头部包含以下内容: #include stdafx.h#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endif(3)在程序的开始处开启报告内存泄漏的开关:_CrtSetD

10、bgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);对于 MFC 工程, MFC 已经做了相关的工作, 只需要确认在每个.cpp 文件的头部包含上述第(2)点的内容。在某些情况下,需要知道发生内存泄漏的内存块中的内容,但是标准的内存转储只是内存块头部的十六进制形式。为了得到更多的有用信息,需要以用户块类型(_CLIENT_ BLOCK)申请内存,并利用_CrtSetDumpClient 建立用户块型内存的转储函数。具体的说,对于不是从 CObject 继承的类,需要:(1)为每个类/结构指定一个用户块子类型(参考 crtdbg.h)。(2)在申

11、请内存时,采用重载的 new 形式:void* _cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)(参考 MFC 源代码 afxmem.cpp),其中 nType 就是用户块的子类型。(3)创建一个用户块内存转储函数,专门对每种需要转储的子类型进行处理(需要包含dbgint.h)。(4)利用_CrtSetDumpClient 对用户块内存转储函数进行注册(参考 MFC 源代码 dumpinit.cpp)。对于从 CObject 继承来的类,MFC 已经按照上述方法做了基础工作(参考 MFC 源

12、代码 afxmem.cpp、dumpinit.cpp)。要有效转储从 CObject 继承的对象,需要:(1)对每个从 CObject 继承的类重载虚函数 Dump。(2)在程序的初始化部分 加入代码 afxDump.SetDepth(1)来开启深度转储。2.3 利用 Purify 和 Insure+查找运行时内存缺陷Rational Purify 和 Parasoft Insure+ 是用于运行时错误检查的工具。Purify 主要检测:数组内存越界读/写,使用未初始化的内存,对已释放的内存进行读/写,内存泄漏等。Insure+利用其专利技术(源码插装和运行时指针跟踪)能够发现大量的内存操作错

13、误,报告错误的源代码行和执行轨迹。根据笔者的测试(基于 98 个有各种内存错误的 C+程序,涵盖了典型情形),Insure+ 6.1 都能准确检测。3 利用 VC+环境的调试和诊断功能,检查和发现常见内存缺陷理解常见的内存缺陷问题以及在 VC+环境下的症状,能辅助我们减少问题的发生和及时修改问题。从错误的表现形式上看, 和堆栈有关的错误主要分为两大类:堆栈溢出和函数返回信息被破坏。(1)堆栈溢出(overflow)此类错误主要有两种情形:1)过大的局部变量。缺省情况下 Windows 为每个线程保留 1M 堆栈空间。在菜单 Project-Properties-Configuration Pr

14、operties - Linker-System 中可以看到 Stack Reserve Size 选项可以调整保留的堆栈空间大小。2)递归调用层数过深。在调试过程中,调用堆栈(call stack)窗口中可以发现函数递归调用的模式。(2)函数返回信息被破坏此类错误主要有两种情形:1)对局部变量的写操作超出了范围(上溢)。在调试过程中,函数堆栈被破坏掉的明显标志是无法显示调用堆栈,并且错误发生在被调用函数即将返回的位置。2)在调用函数和被调用函数之间如果出现了函数参数的不匹配或者调用规范的不一致。为了检查此类错误,应该在代码编译时打开/GS、/RTCs 开关(在菜单 Project-Prope

15、rties-Configuration Properties- C/C+-Code Generation 下设置)。另外一类错误是动态内存错误。典型的情况如下:(1)内存写越界。在调试版本中,如果是写上溢,就会收到“Damage:after block.”的跟踪消息,如果是写下溢出就会收到“Damage: before block.”的跟踪消息。(2)删除不合法指针。在调试版本中,删除未初始化的指针或者非堆指针时,会收到_CrtIsValidHeapPointer 断言错误。(3)多次释放。在调试版本中,如果多次删除同一指针, 会收到_BLOCK_TYPE_IS_VALID 断言错误。要防止此

16、类错误,应在 delete 某个指向动态内存的指针后立即将其置为空。4 利用 Windows 结构化异常处理机制处理发布版本软件的内存崩溃在程序的发布阶段,应尽量减少程序错误尤其是内存崩溃。如果崩溃了,应该“优雅”地退出,尽量收集程序崩溃时的运行信息以帮助程序供应商后续的调试。要捕捉内存非法访问并获知非法访问的指令地址、寄存器内容等信息,需要用到 Windows 的结构化异常处理(Structured Exception Handling,SEH)机制6。MiniDumpWriteDump 是 dbghelp.dll 提供的一个 API 函数(参考 MSDN),用于转储用户模式程序的一些信息(比如堆栈情况等)并存为一个文件(比如.dmp 文件),此文件可以被微软的调试器(VC+或者 WinDBG)利用进行事后调试。使用此函数需要 dbghelp.h、dbghe

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 解决方案

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号