程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 生死疆界(上)--- 在new與delete之間

生死疆界(上)--- 在new與delete之間

編輯:關於VC++

問題源自一段簡單的代碼:

void main()
{
char *p = new char;
cin>>p;
cout<<p[2];
delete p;
}

在以上代碼中,如果你輸入:abcd,那麼如你所望,你會看到"正確"的輸 出"c"。但是會有錯誤提示出現:

Debug Error!

Program: test.exe

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

更離奇的是,如果將代碼改為如下的代碼:

void()
{
char *p = new char;
cin>>p;
cout<<p;
delete p;
}

如果只輸入一個字符a,那麼依然報錯。是不是奇怪,分配了一個字符,輸入了一個 字符,那麼錯在哪裡? 注意,最開始那行Debug Error!說明這是在Debug編譯模式下才有的 提示,如果你換到release頻道,那麼此提示不再出現,你成功得到了"c",仿佛 程序一切正常。

一個奇怪的現象是,如果去掉delete p這條語句,這個運行時錯誤消失了,甚至你在 debug模式下也看不到這個提示。 問題何在?

以前我遇到過這種情況,分析後歸結為一個結論:在debug模式下系統有一定的機制偵測 到內存的非法訪問。然後就放過這個問題。這個結論說了等於沒說,關鍵在於,這種機制的 具體運做過程。這次我下了狠心,不入虎穴,焉得虎子。我決定追進源代碼裡邊去。 把編譯 環境設置成debug模式,很顯然,問題出在delete p上,在這條語句設置斷點,按F5,程序運 行到這條語句前自動暫停,然後按F11。

Welcome to the Source Code World!

首先來到DELOP.CPP文件中,這個文件短小精悍,只有一個函數

void __cdecl operator delete(void *p) _THROW0()
{ // free an allocated object
free(p);
}

沒有任何有用的信息,那就繼續追進free(p)裡。 不一會,我們追到了DBGHEAP.C 中,你從文件名可以看出,這是在debug模式下才能進入的文件。

最後在_CRTIMP void __cdecl _free_dbg(void * pUserData, int nBlockUse )中的這 條語句

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

前受阻。 是不是覺得這這模塊巨 可怕,呵呵,靜下心來,很簡單,因為有if存在,那麼CheckBytes()一定是執行某種檢驗 ,如果檢驗失敗,調用_RPT3()函數 在MSDN中,對_RPT函數族有這樣的解釋:

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

_RPT3的作用就是產生一個錯誤報告。

好了,知道了這一點就足夠了,它對我們來說沒什麼意義了。那麼只剩下CheckBytes了, 深呼吸幾口,好了,讓我們進去吧。

static int __cdecl CheckBytes(unsigned char * pb, unsigned char bCheck, size_t nSize)
{
    int bOkay = TRUE;
    while (nSize--)
    {
      if (*pb++ != bCheck)
      {
        _RPT3(_CRT_WARN, "memory check error at 0x%08X = 0x%02X, should be 0x%02X.\n",
          (BYTE *)(pb-1),*(pb-1), bCheck);
        bOkay = FALSE;
      }
    }
    return bOkay;
}

你看到了,這個函數只調用了_RPT3,再也沒有其他的調用,看來,我們到頭了。 下面是微軟的程序員為這個函數寫的注釋的一部分:

*Purpose:
*    verify byte range set to proper value
*Return:
*    TRUE - if all bytes in range equal bcheck
*    FALSE otherwise

再明顯不過了,這個函數檢驗一定范圍的位是否設定為了 正確的值(就是傳進來的那麼bCheck),如果正確,返回bOkay=TRUE,否則,返回 bOkay=FALSE. 都挖完了,再也沒有任何有用的信息,我們仍舊不知道微軟是如何進行校驗的 ,眼前依然一片黑暗。如果還有黎明的曙光,那麼只能從傳入的參數身上發出,呵呵,它們 三肩負著我們的厚望啊。看看第一個參數unsigned char* pb。 if (*pb++ != bCheck)這條 語句告訴我們要將pb所指內存地址的指與bCheck比較,那麼我們還有最後一線希望:直接監 視內存。

欲知後事如何,且聽下回分解 :)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved