谁动了我
指针
作者:l
![](/icons/86338if.gif)
anxi来源:本站收集发布时间:2005-10-19 17:34:17发布人:admin
谁动了我
![](/icons/86338de.gif)
指针?
译者序:
本文介绍了
![](/icons/86338yi.gif)
种在调试过程中寻找悬挂指针(野指针)
![](/icons/86338de.gif)
思路方法
![](/icons/86338dou.gif)
这种思路方法是通过对
![](/icons/86338new.gif)
和delete运算符
![](/icons/86338de.gif)
重载来实现
![](/icons/86338de.gif)
![](/icons/86338dou2.gif)
这种思路方法不是完美
![](/icons/86338de.gif)
![](/icons/86338dou.gif)
它是以调试期
![](/icons/86338de.gif)
内存泄露为代价来实现
![](/icons/86338de.gif)
![](/icons/86338dou.gif)
![](/icons/86338yinwei.gif)
文中出现
![](/icons/86338de.gif)
代码是绝不能出现在
![](/icons/86338yi.gif)
个最终发布
![](/icons/86338de.gif)
软件Software产品中
![](/icons/86338de.gif)
![](/icons/86338dou.gif)
只能在调试时使用
![](/icons/86338dou2.gif)
在VC中
![](/icons/86338dou.gif)
在调试环境下
![](/icons/86338dou.gif)
可以简单
![](/icons/86338de.gif)
通过把
![](/icons/86338new.gif)
替换成DEBUG_NEW来实现功能更强更方便
![](/icons/86338de.gif)
指针检测
![](/icons/86338dou.gif)
详情可参考MSDN
![](/icons/86338dou2.gif)
DEBUG_NEW
![](/icons/86338de.gif)
实现思路和本文有相通
![](/icons/86338de.gif)
地方
![](/icons/86338dou.gif)
因此文章中介绍
![](/icons/86338de.gif)
思路方法虽然不是最佳
![](/icons/86338de.gif)
![](/icons/86338dou.gif)
但还算实用
![](/icons/86338dou.gif)
更重要
![](/icons/86338de.gif)
是
![](/icons/86338dou.gif)
它提供给我们
![](/icons/86338yi.gif)
种新
![](/icons/86338de.gif)
思路
介绍:
前几天发生了这样
![](/icons/86338yi.gif)
件事
![](/icons/86338dou.gif)
我正在调试
![](/icons/86338yi.gif)
个
![](/icons/86338chengxu.gif)
![](/icons/86338dou.gif)
这个
![](/icons/86338chengxu.gif)
用了
![](/icons/86338yi.gif)
大堆乱 7 8糟
![](/icons/86338de.gif)
指针来处理
![](/icons/86338yi.gif)
个链表
![](/icons/86338dou.gif)
最终在
![](/icons/86338yi.gif)
个指向链表结点
![](/icons/86338de.gif)
指针上出了问题
![](/icons/86338dou2.gif)
我们预计它应当指向
![](/icons/86338de.gif)
是
![](/icons/86338yi.gif)
个虚基类
![](/icons/86338de.gif)
对象
![](/icons/86338dou2.gif)
我想到第
![](/icons/86338yi.gif)
个问题是:指针所指
![](/icons/86338de.gif)
地方真
![](/icons/86338de.gif)
有
![](/icons/86338yi.gif)
个对象吗?出问题
![](/icons/86338de.gif)
指针值可以被4整除
![](/icons/86338dou.gif)
并且不是NULL
![](/icons/86338de.gif)
![](/icons/86338dou.gif)
所以可以断定它曾经是
![](/icons/86338yi.gif)
个有效
![](/icons/86338de.gif)
指针
![](/icons/86338dou2.gif)
通过使用Visual Studio
![](/icons/86338de.gif)
内存查看窗口(View->Debug Windows->Memory)我们发现这个指针所指
![](/icons/86338de.gif)
数据是FE EE FE EE FE EE ...这通常意味着内存是曾经是被分配了
![](/icons/86338de.gif)
![](/icons/86338dou.gif)
但现在却处于
![](/icons/86338yi.gif)
种未分配
![](/icons/86338de.gif)
状态
![](/icons/86338dou2.gif)
不知是谁、在什么地方把我
![](/icons/86338de.gif)
指针所指
![](/icons/86338de.gif)
内存区域给释放掉了
![](/icons/86338dou2.gif)
我想要找出
![](/icons/86338yi.gif)
种方案来查出我
![](/icons/86338de.gif)
数据到底是如何会被释放
![](/icons/86338de.gif)
![](/icons/86338dou2.gif)
背景:
我最终通过重载了
![](/icons/86338new.gif)
和delete运算符找到了我丢失
![](/icons/86338de.gif)
数据
![](/icons/86338dou2.gif)
当
![](/icons/86338yi.gif)
个
![](/icons/86338hanshu.gif)
被
![](/icons/86338diaoyong.gif)
时
![](/icons/86338dou.gif)
参数会首先被压到栈上后
![](/icons/86338dou.gif)
然后返回地址也会被压到栈上
![](/icons/86338dou2.gif)
我们可以在
![](/icons/86338new.gif)
和delete运算符
![](/icons/86338de.gif)
![](/icons/86338hanshu.gif)
中把这些信息从栈上提取出来
![](/icons/86338dou.gif)
帮助我们调试
![](/icons/86338chengxu.gif)
![](/icons/86338dou2.gif)
代码:
在经历了几次
![](/icons/86338cuowu.gif)
![](/icons/86338de.gif)
猜测后
![](/icons/86338dou.gif)
我决定求助于重载
![](/icons/86338new.gif)
和delete运算符来帮我找到我
![](/icons/86338de.gif)
指针所指向
![](/icons/86338de.gif)
数据
![](/icons/86338dou2.gif)
下面
![](/icons/86338de.gif)
![](/icons/86338new.gif)
运算符
![](/icons/86338de.gif)
实现把返回地址从栈上提了出来
![](/icons/86338dou2.gif)
这个返回地址位于传递过来
![](/icons/86338de.gif)
参数和第
![](/icons/86338yi.gif)
个局部变量
![](/icons/86338de.gif)
地址的间
![](/icons/86338dou2.gif)
编译器
![](/icons/86338de.gif)
设置、
![](/icons/86338diaoyong.gif)
![](/icons/86338hanshu.gif)
![](/icons/86338de.gif)
思路方法、计算机
![](/icons/86338de.gif)
体系结构都会引响到这个返回地址
![](/icons/86338de.gif)
实际位置
![](/icons/86338dou.gif)
所以您在使用下面代码
![](/icons/86338de.gif)
时候
![](/icons/86338dou.gif)
要根据您
![](/icons/86338de.gif)
实际情况做
![](/icons/86338yi.gif)
些调整
![](/icons/86338dou2.gif)
![](/icons/86338yi.gif)
旦
![](/icons/86338new.gif)
运算符获得了返回地址
![](/icons/86338dou.gif)
它就在将要实际分配
![](/icons/86338de.gif)
内存前面分配额外
![](/icons/86338de.gif)
16个字节
![](/icons/86338de.gif)
空间来存放这个返回地址和实际
![](/icons/86338de.gif)
分配
![](/icons/86338de.gif)
内存大小
![](/icons/86338dou.gif)
并且把实际要分配
![](/icons/86338de.gif)
内存块首地址返回
![](/icons/86338dou2.gif)
对于delete运算符
![](/icons/86338dou.gif)
你可以看到
![](/icons/86338dou.gif)
它不再释放空间
![](/icons/86338dou2.gif)
它用和
![](/icons/86338new.gif)
同样
![](/icons/86338de.gif)
思路方法把返回地址提取出来
![](/icons/86338dou.gif)
写到实际分配空间大小
![](/icons/86338de.gif)
后面(译者注:就是上面分配
![](/icons/86338de.gif)
16个字节
![](/icons/86338de.gif)
第9到第12个字节)
![](/icons/86338dou.gif)
在最后 4个字节中填上DE AD BE EF(译者注: 4个十 6进制数
![](/icons/86338dou.gif)
当成单词来看正好是dead beef
![](/icons/86338dou.gif)
用来表示内存已释放真是很形象!)
![](/icons/86338dou.gif)
并且把剩余
![](/icons/86338de.gif)
空间(译者注:就是原本实际应该分配而现在应该要释放掉
![](/icons/86338de.gif)
空间)都填上
![](/icons/86338yi.gif)
个重复
![](/icons/86338de.gif)
值
![](/icons/86338dou2.gif)
现在
![](/icons/86338dou.gif)
如果
![](/icons/86338chengxu.gif)
由于
![](/icons/86338yi.gif)
个
![](/icons/86338cuowu.gif)
![](/icons/86338de.gif)
指针而出错
![](/icons/86338dou.gif)
我只需打开内存查看窗口
![](/icons/86338dou.gif)
找到出错
![](/icons/86338de.gif)
指针所指
![](/icons/86338de.gif)
地方
![](/icons/86338dou.gif)
再往前找16个字节
![](/icons/86338dou2.gif)
这里
![](/icons/86338de.gif)
值就是
![](/icons/86338diaoyong.gif)
![](/icons/86338new.gif)
运算符
![](/icons/86338de.gif)
地址
![](/icons/86338dou.gif)
接下来 4个字节就是实际分配
![](/icons/86338de.gif)
内存大小
![](/icons/86338dou.gif)
第 3个 4个字节是
![](/icons/86338diaoyong.gif)
delete运算符
![](/icons/86338de.gif)
地址
![](/icons/86338dou.gif)
最后 4个字节应该是DE AD BE EF
![](/icons/86338dou2.gif)
接下
![](/icons/86338de.gif)
实际分配过
![](/icons/86338de.gif)
内存内容应该是77 77 77 77
![](/icons/86338dou2.gif)
要通过这两个返回地址在源
![](/icons/86338chengxu.gif)
中分别找到对应
![](/icons/86338de.gif)
![](/icons/86338new.gif)
和delete
![](/icons/86338dou.gif)
可以这样做:首先把表示地址
![](/icons/86338de.gif)
4个字节
![](/icons/86338de.gif)
内容倒序排
![](/icons/86338yi.gif)
下
![](/icons/86338dou.gif)
这样才能得到真正
![](/icons/86338de.gif)
地址
![](/icons/86338dou.gif)
这里
![](/icons/86338yinwei.gif)
在Intel平台上字节序是低位在前
![](/icons/86338de.gif)
![](/icons/86338dou2.gif)
下
![](/icons/86338yi.gif)
步
![](/icons/86338dou.gif)
在源代码上右击点击
![](/icons/86338dou.gif)
选“Go To Diassembly”
![](/icons/86338dou2.gif)
在反汇编
![](/icons/86338de.gif)
窗口上
![](/icons/86338de.gif)
左边
![](/icons/86338yi.gif)
栏就是机器代码对应
![](/icons/86338de.gif)
内存地址
![](/icons/86338dou2.gif)
按Ctrl + G或选择Edit->Go To...并输入你找到
![](/icons/86338de.gif)
地址的
![](/icons/86338yi.gif)
![](/icons/86338dou2.gif)
反汇编
![](/icons/86338de.gif)
窗口就将滚动到对应
![](/icons/86338de.gif)
![](/icons/86338new.gif)
或delete
![](/icons/86338de.gif)
![](/icons/86338hanshu.gif)
![](/icons/86338diaoyong.gif)
位置
![](/icons/86338dou2.gif)
要回到源
![](/icons/86338chengxu.gif)
只需再次右键单击
![](/icons/86338dou.gif)
选择“Go To Source”
![](/icons/86338dou2.gif)
您就可以看到相应
![](/icons/86338de.gif)
![](/icons/86338new.gif)
或delete
![](/icons/86338de.gif)
![](/icons/86338diaoyong.gif)
了
![](/icons/86338dou2.gif)
现在您就可以很方便
![](/icons/86338de.gif)
找出您
![](/icons/86338de.gif)
数据是何时丢失
![](/icons/86338de.gif)
了
![](/icons/86338dou2.gif)
至于要找出为什么delete会被
![](/icons/86338diaoyong.gif)
![](/icons/86338dou.gif)
就要靠您自己了
![](/icons/86338dou2.gif)
#
![](/icons/86338include.gif)
<MALLOC.H>
void * :perator
![](/icons/86338new.gif)
(size_t size)
{
![](/icons/86338int.gif)
stackVar;
unsigned long stackVarAddr = (unsigned long)&stackVar;
unsigned long argAddr = (unsigned long)&size;
void ** retAddrAddr = (void **)(stackVarAddr/2 + argAddr/2 + 2);
void * retAddr = * retAddrAddr;
unsigned char *retBuffer = (unsigned char*)malloc(size + 16);
mem
![](/icons/86338set.gif)
(retBuffer, 0, 16);
memcpy(retBuffer, &retAddr,
![](/icons/86338sizeof.gif)
(retAddr));
memcpy(retBuffer + 4, &size,
![](/icons/86338sizeof.gif)
(size));
![](/icons/86338return.gif)
retBuffer + 16;
}
void :perator delete(void *buf)
{
![](/icons/86338int.gif)
stackVar;
![](/icons/86338if.gif)
(!buf)
![](/icons/86338return.gif)
;
unsigned long stackVarAddr = (unsigned long)&stackVar;
unsigned long argAddr = (unsigned long)&buf;
void ** retAddrAddr = (void **)(stackVarAddr/2 + argAddr/2 + 2);
void * retAddr = * retAddrAddr;
unsigned char* buf2 = (unsigned char*)buf;
buf2 -= 8;
memcpy(buf2, &retAddr,
![](/icons/86338sizeof.gif)
(retAddr));
size_t size;
buf2 -= 4;
memcpy(&size, buf2,
![](/icons/86338sizeof.gif)
(buf2));
buf2
![](/icons/86338jiadeng.gif)
8;
buf2[0] = 0xde;
buf2[1] = 0xad;
buf2[2] = 0xbe;
buf2[3] = 0xef;
buf2
![](/icons/86338jiadeng.gif)
4;
mem
![](/icons/86338set.gif)
(buf2, 0x7777, size);
// deallocating destroys saved addresses, so dont
// buf -= 16;
// free(buf);
}
其它值得关注
![](/icons/86338de.gif)
地方:
这段代码同样可以用于内存泄露
![](/icons/86338de.gif)
检测
![](/icons/86338dou2.gif)
只需修改delete运算符使它真正
![](/icons/86338de.gif)
去释放内存
![](/icons/86338dou.gif)
并且在
![](/icons/86338chengxu.gif)
退出前
![](/icons/86338dou.gif)
用__heapwalk遍历所有已分配
![](/icons/86338de.gif)
内存块并把
![](/icons/86338diaoyong.gif)
![](/icons/86338new.gif)
![](/icons/86338de.gif)
地址提取出来
![](/icons/86338dou.gif)
这就将得到
![](/icons/86338yi.gif)
份没有被delete匹配
![](/icons/86338de.gif)
![](/icons/86338new.gif)
![](/icons/86338diaoyong.gif)
列表
![](/icons/86338dou2.gif)
还要注意
![](/icons/86338de.gif)
是:这里列出
![](/icons/86338de.gif)
代码只能在调试
![](/icons/86338de.gif)
时候去使用
![](/icons/86338dou.gif)
如果你把它段代码放到最终
![](/icons/86338de.gif)
产品中
![](/icons/86338dou.gif)
会导致
![](/icons/86338chengxu.gif)
运行时内存被大量
![](/icons/86338de.gif)
消耗