C++编码中淘汰内存缺陷的要领和东西
当前位置:以往代写 > C/C++ 教程 >C++编码中淘汰内存缺陷的要领和东西
2019-06-13

C++编码中淘汰内存缺陷的要领和东西

C++编码中淘汰内存缺陷的要领和东西

副标题#e#

C++语言是桌面系统,尤其是系统软件、大型应用软件的主流开拓语言。C++语言以其机动性著称,同时也更巨大。操作C++编写结实的代码,更具有挑战性。C++答允动态内存打点, 同时也容易导致更多和内存相关的问题。一般而言, 除了系统设计上的缺陷, 基于C++的软件的缺陷和错误大部门都和内存缺陷(主要包罗内存会见错误和内存泄漏两类)相关。 所以,消除代码中的内存相关缺陷,成为措施员编写、调试、维护代码中的任务,也是担保软件质量的要害。

本文的事情基于“863”打算项目“面向网络海量空间信息的大型GIS”课题。该系统是基于C++/MFC编写,开拓情况是Visual Studio .net 2003。本文基于此项目标工程实践,总结了如何利用C++语言机制、开拓情况和相关质量担保东西来防范、发明各类编译期、运行期和内存相关的缺陷的要领和东西。

1 遵循C++相关的编码类型和习用法,防范缺陷

编码类型是语言相关的法则,是颠末实践总结出来的履历。精采的编程尺度将有效地辅佐开拓人员制止开拓有潜在危险的代码。一般来说,为了淘汰内存缺陷,应该遵循下列编码法则[1]:

(1)基类可能带有虚函数的类应该将其析构函数声明为虚函数。

(2)在结构函数中防备内存泄漏,在析构函数中不要抛出异常。

(3)利用对应形式的new和delete。即:用delete来释放new申请的内存,delete[]释放new[]申请的内存。

(4)指针在利用前必需初始化,指向动态内存的指针在释放后该当即置为空。

(5)假如类结构函数中分派了资源,那么需要显式提供拷贝结构函数和赋值操纵符,而且在析构函数中释放资源。

值得重视的是C++中的习用法RAII。RAII焦点思想是操作工具来打点资源,在工具的结构函数中获取资源,在其析构函数中释放资源[2]。为了担保动态申请的内存能在纵然呈现异常的环境下仍能释放,较量抱负的要领是利用局部变量来打点动态内存的所有权(ownership),就是所谓的智能指针。STL中的auto_ptr就是为办理资源所有权问题设计的,可是缺少对引用数和数组的支持而且不能用在STL容器中。Boost库[3]提供的智能指针相对成熟,实用代价高。个中,shared_ptr线程安详而且可以用在STL容器中。详细示例参考文献[3]。

1.1 编码类型查抄东西 CodeWizard

CodeWizard可以或许对源措施直接举办自动扫描、阐明和查抄。一旦发明违例,发生信息奉告与哪条法则不符并作出表明。以CodeWizard 4.3 为例,个中内置了高出500条编码尺度。CodeWizard可以选择对付当前的工程执行哪些编码尺度。CodeWizard可以和VC++细麋集成,安装完毕今后,VC++中有CodeWizard东西条。

1.2 代码查抄东西 PC-Lint

PC-Lint可查抄编译器不易发明的错误。PC-Lint可对100多个C库函数举办查抄,可以发明尺度C/C++代码中的1 000多个常见错误。要把PC-lint和Visual Studio集成在一起,需要本身设置。Jon Zyzyck提供了一个陈诉生成器,可以辅佐完成这个事情。可在http://www.ddj.com下载。文献[4]说明白如安在VC++情况中集成PC-Lint。


#p#副标题#e#

2 操作语言机制、开拓情况和相关东西以防范和发明内存缺陷

发明问题是办理问题的前提。相对付修复内存缺陷,发明内存缺陷并精确定位导致缺陷的代码更为费时艰辛。赶早精确地发明内存缺陷,对付提高开拓效率很是重要。

2.1 操作断言赶早袒露内存缺陷

断言是布尔调试语句,用来检测在措施运行的时候某一条件的值是否总为真。断言常常用来确认函数的输入、输出,查抄工具的当前状态是否正当等。 在以下的场景利用断言可以辅佐发明和内存犯科会见相关的错误:

(1)验证指针是否可读/写。在函数的进口处,常常需要验证指针所指向的内容区域是否可读/写。 凡是回收assert(p!= NULL)的检测形式。 可是,指针的值不为空并不代表指针指向了正当可读/写内存。Win32 API提供了函数IsBadReadPtr、IsBadWritePtr、IsBadStringPtr、IsBadCodePtr用来检测指针指向的内存区域是否可读/写。C运行时库提供了_CrtIs ValidPointer、_CrtIsValidHeapPointer等函数,MFC库提供了AfxIsValidAddress、AfxIsValidString函数来完成雷同成果。

(2)对基于MFC的措施,ASSERT_VALID宏通过挪用重载的AssertValid函数来确定指向CObject派生类工具的指针是否有效。ASSERT_VALID宏主要挪用了AfxIsValidAddress函数和CObject派生类工具的AssertValid函数(参考MFC源代码afx.h、objcore.cpp)。

2.2 操作C运行时刻库查抄内存泄漏

VC++的C运行库(CRT)提供了遍及的成果,辅佐用户检测内存泄漏。CRT提供了_CrtMemCheckPoint、_CrtDump MemoryLeaks、_CrtSetDbgFlag等函数来辅佐调试内存泄漏。

对付非MFC的工程, 要开启有效的内存泄漏陈诉成果, 需要举办如下配置:

(1)在StdAfx.h的头部添加如下代码并开启编译器/Yu 选项:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#define DEBUG_NEW new(_NORMAL_BLOCK, THIS_FILE, __LINE__)

(2)确保在每个.cpp文件的头部包括以下内容:

#include "stdafx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#p#副标题#e#

(3)在措施的开始处开启陈诉内存泄漏的开关:

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

对付MFC工程, MFC已经做了相关的事情, 只需要确认在每个.cpp文件的头部包括上述第(2)点的内容。

#p#分页标题#e#

在某些环境下,需要知道产生内存泄漏的内存块中的内容,可是尺度的内存转储只是内存块头部的十六进制形式。为了获得更多的有用信息,需要以用户块范例(_CLIENT_ BLOCK)申请内存,并操作_CrtSetDumpClient成立用户块型内存的转储函数。详细的说,对付不是从CObject担任的类,需要:

(1)为每个类/布局指定一个用户块子范例(参考crtdbg.h)。

(2)在申请内存时,回收重载的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源代码 afxmem.cpp、dumpinit.cpp)。要有效转储从CObject担任的工具,需要:(1)对每个从CObject担任的类重载虚函数Dump。(2)在措施的初始化部门 插手代码 afxDump.SetDepth(1)来开启深度转储。

2.3 操作Purify和Insure++查找运行时内存缺陷

Rational Purify和Parasoft Insure++ 是用于运行时错误查抄的东西。Purify主要检测:数组内存越界读/写,利用未初始化的内存,对已释放的内存举办读/写,内存泄漏等。Insure++操作其专利技能(源码插装和运行时指针跟踪)可以或许发明大量的内存操纵错误,陈诉错误的源代码行和执行轨迹。按照笔者的测试(基于98个有各类内存错误的C++措施,涵盖了典范景象),Insure++ 6.1都能精确检测。

#p#副标题#e#

3 操作VC++情况的调试和诊断成果,查抄和发明常见内存缺陷

领略常见的内存缺陷问题以及在VC++情况下的症状,能帮助我们淘汰问题的产生和实时修改问题。

从错误的表示形式上看, 和仓库有关的错误主要分为两大类:仓库溢出和函数返复书息被粉碎。

(1)仓库溢出(overflow)

此类错误主要有两种景象:

1)过大的局部变量。缺省环境下Windows为每个线程保存1M仓库空间。在菜单Project->Properties->Configuration Properties -> Linker->System中可以看到Stack Reserve Size选项可以调解保存的仓库空间巨细。

2)递归挪用层数过深。在调试进程中,挪用仓库(call stack)窗口中可以发明函数递归挪用的模式。

(2)函数返复书息被粉碎

此类错误主要有两种景象:

1)对局部变量的写操纵超出了范畴(上溢)。在调试进程中,函数仓库被粉碎掉的明明符号是无法显示挪用仓库,而且错误产生在被挪用函数即将返回的位置。

2)在挪用函数和被挪用函数之间假如呈现了函数参数的不匹配可能挪用类型的纷歧致。

为了查抄此类错误,应该在代码编译时打开/GS、/RTCs开关(在菜单Project->Properties->Configuration Properties-> C/C++->Code Generation下配置)。

别的一类错误是动态内存错误。典范的环境如下:

(1)内存写越界。在调试版本中,假如是写上溢,就会收到“Damage:after block…”的跟踪动静,假如是写下溢出就会收到“Damage: before block…”的跟踪动静。

(2)删除不正当指针。在调试版本中,删除未初始化的指针可能非堆指针时,会收到_CrtIsValidHeapPointer断言错误。

(3)多次释放。在调试版本中,假如多次删除同一指针, 会收到_BLOCK_TYPE_IS_VALID断言错误。要防备此类错误,应在delete某个指向动态内存的指针后当即将其置为空。

4 操作Windows布局化异常处理惩罚机制处理惩罚宣布版本软件的内存瓦解

在措施的宣布阶段,应只管淘汰措施错误尤其是内存瓦解。假如瓦解了,应该“优雅”地退出,只管收集措施瓦解时的运行信息以辅佐措施供给商后续的调试。要捕获内存犯科会见并获知犯科会见的指令地点、寄存器内容等信息,需要用到Windows的布局化异常处理惩罚(Structured Exception Handling,SEH)机制[6]。MiniDumpWriteDump是dbghelp.dll提供的一个 API函数(参考MSDN),用于转储用户模式措施的一些信息(好比仓库环境等)并存为一个文件(好比.dmp文件),此文件可以被微软的调试器(VC++可能WinDBG)操作举办过后调试。利用此函数需要dbghelp.h、dbghelp.lib和dbghelp.dll(这些文件可以在Windows Platform SDK中找到)。

#p#分页标题#e#

要过后按照.dmp文件调试代码,需要为宣布版本软件发生debug symbols (pdb)文件(打开编译器/DEBUG选项)。在拿到.dmp文件今后,用VC++打开.dmp文件,然后调试执行(按F5键)。这样,瓦解现场就会重现。文献[5]基于上述的要领实现了瓦解陈诉系统。

5 结论

实践证明,在上述要领和东西支持下的淘汰软件内存缺陷的要领和东西,可以有效防备和查找代码中的内存错误和内存泄漏,而且能和开拓人员日常编码无缝团结,执行起来很是高效。上述要领共同单位测试、代码评审、逐日构建、Bug追踪等法子,形成了一个高效的质量担保流程,在我们的大型平台软件开拓进程中起到了重要浸染。

    关键字:

在线提交作业