0
这篇要做一点改变,本来计划是怎么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判断成立就中断,可以让你不修改代码调试。但是目前我用下来感觉这个的效率太低,会让程序执行速度下降很多,不如把条件写在代码里。如果是你这个条件断点是设置在一个访问上万次的地方,比如某个循环里面,那还是不要用这个,太慢。
————————————————
版权声明:本文为CSDN博主「Mr_L_Y」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luoyu510183/article/details/85030068
关闭 返回顶部
联系我们
Copyright © 2011. 聚财吧. All rights reserved.