环境:Windows XP SP3,VC2008
在开发过程中,偶尔会遇到程序提示 HEAP异常的情况
以下讨论限于Debug模式
常见提示如下
vc的输出窗口提示:
翻译过来就是 某个已经释放的堆空间被修改了,就是某块动态申请(malloc/new)的内存,在它被释放(free/delete)后,又被使用了。
比如编写了如下的代码,就会有这个提示
以上代码比较简单,可以很容易的看出问题出在哪里,当代码量比较大的时候,就不是一眼就能看出来了。
在以上代码中,调试器断在了line31,那是一句申请空间的语句,当申请空间的时候,crt会进行一定的检查,所以就发现了这个heap 错误。
为了查找是哪一个地方的内存错误,需要借助 VC 提供的几个 heap debug 函数。
在程序的开始部分加入
它们会把malloc/free函数映射到dbg版的函数上去,那几个函数有调用时的文件名和函数行数信息,方便定位代码位置
修改后的代码如下
_CrtSetDbgFlag() 是设置调试标志,这里设置了_CRTDBG_CHECK_ALWAYS_DF(每一次malloc/free等操作,都对内存进行检查) 和 _CRTDBG_DELAY_FREE_MEM_DF(释放free掉的内存还是放在heap list里,方便以后找出这块内存的具体位置)。
_CrtMemCheckpoint() 设置内存检测点,类似于给当前heap内存做一个快照。快照信息就放在它的参数里面,如memStat里。
准备工作做完后,再次运行这个程序,heap 错误的提示又再次出现了,下面进行分析
红色圈起来的地方值得注意,一个是出问题的内存的位置,一个是出问题的内存的大小。
刚才提到了一个变量memStat,这是一个结构体变量,定义如下:
pBlockHeader是一个结构体指针,指向内存块链表
lCounts介绍了各种类型(_FREE_BLOCK、_NORMAL_BLOCK、_CRT_BLOCK、_IGNORE_BLOCK、_CLIENT_BLOCK)的内存块的数量,这些内存块可以通过遍历pBlockHeader指向的链表得到
其他的这里用不到,就不介绍了,详情参考 MSDN
_FREE_BLOCK 类型的内存块就是出问题的内存块,通过遍历pBlockHeader就可以找到,pBlockHeader类型定义如下
由以上定义可以看出来,pBlockHeader指向一个双向链表,其中
szFileName 是申请空间时的文件名
nLine 是申请时的代码行号
nDataSize 是申请的内存大小
nBlockUse 是内存块类型(_FREE_BLOCK、_NORMAL_BLOCK、_CLIENT_BLOCK等)
lRequest 是申请次数,查找内存泄漏的时候用的到
gap/anotherGap 是填充空间,做内存边界用,可用来检测上下溢出
data 这块就是我们使用的内存了,data的地址和出错时提示的地址是一个内容
通过定义,可以发现 _CrtMemBlockHeader 的大小是32即0x20,在pBlockHeader里查找错误地址时需要查找 [xxxx – 0x20] 这个地址。
在这个例子里,就是查找[0x00035C68 – 0x20 = 0x00035C48],在Watch里跟踪pBlockHeader,很快就可以找到这个地址
红色圈起来的地方就可以找到出问题的 heap 地址了
对照源代码
就能发现是哪个地方申请的变量出问题了
跟踪这个变量,一般很快就能发现问题了
demo下载:http://download.csdn.net/detail/xkxjy/3869116