生死疆界--- 在new与delete之间

问题源自一段简单的代码:

1. void main()
2. {
3. char *p = new char;
4. cin>>p;
5. cout<<p[2];
6. delete p;
7. }

在以上代码中,如果你输入:abcd,那么如你所望,你会看到"正确"的输出"c"。但是会有错误提示出现:

    Debug Error!

    Program: test.exe

    DAMAGE: after Normal block(#64) at 0x003429f8

更离奇的是,如果将代码改为如下的代码:

1. void()
2. {
3. char *p = new char;
4. cin>>p;
5. cout<<p;
6. delete p;
7. }

如果只输入一个字符a,那么依然报错。是不是奇怪,分配了一个字符,输入了一个字符,那么错在哪里? 注意,最开始那行Debug Error!说明这是在Debug编译模式下才有的提示,如果你换到release频道,那么此提示不再出现,你成功得到了"c",仿佛程序一切正常。

一个奇怪的现象是,如果去掉delete p这条语句,这个运行时错误消失了,甚至你在debug模式下也看不到这个提示。 问题何在?

以前我遇到过这种情况,分析后归结为一个结论:在debug模式下系统有一定的机制侦测到内存的非法访问。然后就放过这个问题。这个结论说了等于没说,关键在于,这种机制的具体运做过程。这次我下了狠心,不入虎穴,焉得虎子。我决定追进源代码里边去。 把编译环境设置成debug模式,很显然,问题出在delete p上,在这条语句设置断点,按F5,程序运行到这条语句前自动暂停,然后按F11。

    Welcome to the Source Code World!

首先来到DELOP.CPP文件中,这个文件短小精悍,只有一个函数

1. void __cdecl operator delete(void *p) _THROW0()
2. // free an allocated object
3. free(p);
4. }

没有任何有用的信息,那就继续追进free(p)里。 不一会,我们追到了DBGHEAP.C中,你从文件名可以看出,这是在debug模式下才能进入的文件。

最后在_CRTIMP void __cdecl _free_dbg(void * pUserData, int nBlockUse )中的这条语句

1. if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize))
2. _RPT3(_CRT_ERROR, "DAMAGE: after %hs block (#%d) at 0x%08X.\n",
3. szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
4. pHead->lRequest,
5. (BYTE *) pbData(pHead));

前受阻。 是不是觉得这这模块巨可怕,呵呵,静下心来,很简单,因为有if存在,那么CheckBytes()一定是执行某种检验,如果检验失败,调用_RPT3()函数 在MSDN中,对_RPT函数族有这样的解释:

    Track an application''s progress by generating a debug report (debug version only).

_RPT3的作用就是产生一个错误报告。

好了,知道了这一点就足够了,它对我们来说没什么意义了。那么只剩下CheckBytes了,深呼吸几口,好了,让我们进去吧。

01. static int __cdecl CheckBytes(unsigned char * pb, unsigned charbCheck, size_t nSize)
02. {
03. int bOkay = TRUE;
04. while (nSize--)
05. {
06. if (*pb++ != bCheck)
07. {
08. _RPT3(_CRT_WARN, "memory check error at 0x%08X = 0x%02X, should be 0x%02X.\n",
09. (BYTE *)(pb-1),*(pb-1), bCheck);
10. bOkay = FALSE;
11. }
12. }
13. return bOkay;
14. }

你看到了,这个函数只调用了_RPT3,再也没有其他的调用,看来,我们到头了。 下面是微软的程序员为这个函数写的注释的一部分:

1. *Purpose:
2. *       verify byte range set to proper value
3. *Return:
4. *       TRUE - if all bytes in range equal bcheck
5. *       FALSE otherwise

再明显不过了,这个函数检验一定范围的位是否设定为了正确的值(就是传进来的那么bCheck),如果正确,返回bOkay=TRUE,否则,返回bOkay=FALSE. 都挖完了,再也没有任何有用的信息,我们仍旧不知道微软是如何进行校验的,眼前依然一片黑暗。如果还有黎明的曙光,那么只能从传入的参数身上发出,呵呵,它们三肩负着我们的厚望啊。看看第一个参数unsigned char* pb。 if (*pb++ != bCheck)这条语句告诉我们要将pb所指内存地址的指与bCheck比较,那么我们还有最后一线希望:直接监视内存

欲知后事如何,且听下回分解 :)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值