VS2017的C++开发心得(十二)调试——内存溢出和内存泄漏(中)

这篇要做一点改变,本来计划是怎么debug内存溢出,但由于内存溢出的方式各种各样,没有固定的debug方式,调试手段随机应变。所以这篇主要从内存分布来理解内存溢出问题,以及内存溢出可能造成的后果。

首先看看结构体的内存分布,代码如下:

#include <Windows.h>

struct SA
{
	double dArray[2];
	int t;
	SA()
	{
		FillMemory(dArray, sizeof(double), 0x11);
		FillMemory(dArray+1, sizeof(double), 0x22);
		FillMemory(&t, sizeof(int), 0x33);
	}
};
struct SB
{
	double dArray[2];
	SA* sa;
};
int main()
{
	SA testa;
	return testa.t;
}

上面的FillMemory主要是为了让它们的内存更容易区分,这个函数的作用就是在这块内存上填上对应的hex值。这个函数属于Windows标准库的,同样的还有ZeroMemory。

接下来通过内存窗口看看这个SA结构体的内存分布:

这样一看是不是很明了了,结构体的内存都是连续的,double占8字节,int占4字节。内存溢出实际上主要发生在对内存的直接操作上,也就是指针的使用。

下面我演示了一个数组访问越界的操作:

这里已经溢出,但程序还是正常在运行。这正是内存溢出最头疼的地方:溢出了你不一定知道。所以C++是一种不安全的语言,这种指针的东西在C#中都要使用unsafe来访问。当然这种不安全是由于编程人员本身造成的,指针的使用可以有效的避免无用的内存副本创建,加快运行速度。

回到这个程序,现在由于访问越界后testa.t的值被修改成了0x44444444.目前来讲好像一切正常,只是你程序可能跑着跑着,突然感觉这个t的值什么时候被修改了。然后你会搜索所有t的赋值代码,你仍然一无所获。其实是dArray的越界访问造成的。

这时候你就需要用VS的条件断点来找到这个变量被改变的时刻,有两种方式可以添加值更改的条件断点:

或者:

建立好断点以后,只要这个地址下有值变化都会触发以下的断点信息,这时候你可以在调用堆栈里看到具体是哪行代码造成了错误赋值:

上面介绍的值更改断点必须先将程序进入断点,让VS对当前的内存进行一次记录,然后再添加地址信息。

以上只是介绍的这种情况算是溢出里最难处理的情况,因为程序不会马上对这个溢出产生反应,被修改的变量很有可能跑了好一段时间后才会造成程序的异常。这时候程序的崩溃点可能远不是你写的bug的位置。好在VS的功能十分强大,现在解决起来也比较简单了,否则你要一点点的读代码找bug。

溢出的问题就讲到这里,最后说下VS除了值更改断点外还有个表达式断点:

很简单就是加一个表达式让VS判断成立就中断,可以让你不修改代码调试。但是目前我用下来感觉这个的效率太低,会让程序执行速度下降很多,不如把条件写在代码里。如果是你这个条件断点是设置在一个访问上万次的地方,比如某个循环里面,那还是不要用这个,太慢。

 

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值