专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »C 教程 » 鼠标指针不动了:谁动了我的指针 »正文

鼠标指针不动了:谁动了我的指针

来源: 发布时间:星期四, 2009年2月12日 浏览:94次 评论:0


谁动了我指针?

译者序:
  本文介绍了种在调试过程中寻找悬挂指针(野指针)思路方法这种思路方法是通过对和delete运算符重载来实现
  这种思路方法不是完美它是以调试期内存泄露为代价来实现文中出现代码是绝不能出现在个最终发布软件Software产品中只能在调试时使用
  在vc中在调试环境下可以简单通过把替换成debug_来实现功能更强更方便指针检测详情可参考msdndebug_实现思路和本文有相通地方因此文章中介绍思路方法虽然不是最佳但还算实用更重要它提供给我们种新思路

介绍:
  前几天发生了这样件事我正在调试这个用了大堆乱 7 8糟指针来处理个链表最终在个指向链表结点指针上出了问题我们预计它应当指向个虚基类对象我想到第个问题是:指针所指地方真个对象吗?出问题指针值可以被4整除并且不是null所以可以断定它曾经是个有效指针通过使用visual studio内存查看窗口(view->debug windows->memory)我们发现这个指针所指数据是fe ee fe ee fe ee ...这通常意味着内存是曾经是被分配了但现在却处于种未分配状态不知是谁、在什么地方把我指针所指内存区域给释放掉了我想要找出种方案来查出我数据到底是如何会被释放

背景:
  我最终通过重载了和delete运算符找到了我丢失数据参数会首先被压到栈上后然后返回地址也会被压到栈上我们可以在和delete运算符中把这些信息从栈上提取出来帮助我们调试

代码:
  在经历了几次猜测后我决定求助于重载和delete运算符来帮我找到我指针所指向数据下面运算符实现把返回地址从栈上提了出来这个返回地址位于传递过来参数和第个局部变量地址的间编译器设置、思路方法、计算机体系结构都会引响到这个返回地址实际位置所以您在使用下面代码时候要根据您实际情况做些调整运算符获得了返回地址它就在将要实际分配内存前面分配额外16个字节空间来存放这个返回地址和实际分配内存大小并且把实际要分配内存块首地址返回
  对于delete运算符你可以看到它不再释放空间它用和同样思路方法把返回地址提取出来写到实际分配空间大小后面(译者注:就是上面分配16个字节第9到第12个字节)在最后 4个字节中填上de ad be ef(译者注: 4个十 6进制数当成单词来看正好是dead beef用来表示内存已释放真是很形象!)并且把剩余空间(译者注:就是原本实际应该分配而现在应该要释放掉空间)都填上个重复
  现在如果由于指针而出错我只需打开内存查看窗口找到出错指针所指地方再往前找16个字节这里值就是运算符地址接下来 4个字节就是实际分配内存大小第 3个 4个字节是delete运算符地址最后 4个字节应该是de ad be ef接下实际分配过内存内容应该是77 77 77 77
  要通过这两个返回地址在源中分别找到对应和delete可以这样做:首先把表示地址 4个字节内容倒序排这样才能得到真正地址这里el平台上字节序是低位在前在源代码上右击点击选“go to diassembly”在反汇编窗口上左边栏就是机器代码对应内存地址按ctrl + g或选择edit->go to...并输入你找到地址的反汇编窗口就将滚动到对应或delete位置要回到源只需再次右键单击选择“go to source”您就可以看到相应或delete
  现在您就可以很方便找出您数据是何时丢失至于要找出为什么delete会被就要靠您自己了
  # <malloc.h>

  void * :perator (size_t size)
  {
     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(retbuffer, 0, 16);

    memcpy(retbuffer, &retaddr, (retaddr));

    memcpy(retbuffer + 4, &size, (size));

     retbuffer + 16;
  }

  void :perator delete(void *buf)
  {
     stackvar;
    (!buf)
      ;

    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, (retaddr));

    size_t size;

    buf2 -= 4;

    memcpy(&size, buf2, (buf2));

    buf2 8;

    buf2[0] = 0xde;
    buf2[1] = 0xad;
    buf2[2] = 0xbe;
    buf2[3] = 0xef;

    
    buf2 4;

    mem(buf2, 0x7777, size);

    // deallocating destroys saved addresses, so dont
    // buf -= 16;
    // free(buf);
  }

其它值得关注地方:
  这段代码同样可以用于内存泄露检测只需修改delete运算符使它真正去释放内存并且在退出前用__heapwalk遍历所有已分配内存块并把地址提取出来这就将得到份没有被delete匹配列表
  还要注意是:这里列出代码只能在调试时候去使用如果你把它段代码放到最终产品中会导致运行时内存被大量消耗

0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: