2008-09-15 117 views
5

衆所周知,Visual C++運行庫會使用特殊的非零標記標記未初始化或剛釋放的內存塊。是否有任何方法可以完全禁用此行爲,而無需手動將所有未初始化的內存設置爲零?自從0xFEEEFEEE != 0以來,它對我有效的非空檢查造成破壞。VC++中未初始化的內存塊

人,也許我應該更好解釋一下。我創建並初始化一個變量(通過新的),並且一切都很好。當我釋放它時(通過刪除),它將指針設置爲0xFEEEFEEE而不是NULL。當我插入適當的檢查NULL,因爲所有管理自己的記憶的好方案應該,我提出了問題,因爲0xFEEEFEEE通過NULL檢查沒有問題。有沒有什麼好辦法,除了刪除它們時手動設置所有指針爲NULL,以檢測內存何時已被釋放?我寧願不使用Boost,只是因爲我不想要開銷,儘管它可能很小,因爲這是我將使用Boost的唯一一件事。

+4

烏姆,刪除不設置指針反正爲NULL。如果你認爲它確實有 – 2009-01-24 08:34:51

+1

的錯誤,那麼你的程序有一個錯誤,幾乎所有的刪除操作都應該後面跟着一行,它明確地將指針設置爲NULL。更好的是,更頻繁地使用智能指針。 – 2009-03-31 17:35:08

+0

你說過「當我釋放它(通過刪除)時,它將指針設置爲0xFEEEFEEE」。你能澄清一下嗎?也許你的意思是*指向*的內存被設置爲0xFEEEFEEE?或者你真的認爲指針本身設置爲指向地址0xFEEEFEEE? – 2012-02-08 20:40:21

回答

6

delete的所有指針重置爲NULL不是責任。 另外你不應該改變windows DEBUG運行時的默認內存填充,你應該使用一些像boost::shared_ptr<>這樣的指針。

這就是說,如果你真的想拍你自己在腳下你可以。

可以變化默認通過使用分配器鉤這樣填寫爲Windows 調試運行時。這隻會在HEAP分配的對象上起作用!

int main(int argc,char** arv) 
{ 
    // Call first to register hook  
    _CrtSetAllocHook(&zero_fill); 
    // Do other stuff 
    malloc(100); 
} 


int zero_fill(int nAllocType, 
       void* pvData, 
       size_t nSize, 
       int nBlockUse, 
       long lRequest, 
       const unsigned char *szFileName, 
       int nLine) 
{ 
    /// Very Importaint !! 
    /// infinite recursion if this is removed !! 
    /// _CRT_BLOCK must not do any thing but return TRUE 
    /// even calling printf in the _CRT_BLOCK will cause 
    /// infinite recursion 
    if (nBlockUse == _CRT_BLOCK) 
    return(TRUE); 
    switch(nAllocType) 
    { 
    case _HOOK_ALLOC: 
    case _HOOK_REALLOC: 
    // zero initialize the allocated space. 
    memset(pvData,0,nSize); 
    break; 
    case _HOOK_FREE: 
    break; 
    } 
    return TRUE; 
} 
+0

這實際上無意中爲我提供了我想要的解決方案:我可以在_HOOK_FREE上將pvData設置爲NULL,並且不會遇到針對我的指針地址的0xFEEEFEEE問題。 – 2008-09-16 05:16:54

18

當您創建一個指針時,將其初始化爲NULL。同樣在delete之後。取決於未初始化數據的值(除少數特定情況外)正在尋求麻煩。

通過使用智能指針類(如boost::shared_ptr)可以自動處理指針是否被初始化,從而爲自己節省很多頭痛。

+3

完全同意。應始終完成將新內存初始化爲零。作爲一個方面,我注意到一件事是在刪除之前對ptr!= NULL進行不必要的檢查。刪除空指針總是安全的。不是在未初始化的ptr上的情況。 – 2008-09-15 19:49:49

0

我敢肯定,你不能在這裏禁用Visual Studio的默認值,即使你這樣做了,那麼這個值就只是分配內存之前的內存。

你最好只是習慣於先把它們設置爲0,它只有2個額外的字符。

int *ptr=0; 

您也可以使用NULL宏,它被定義爲0(而不是默認的,包括,東西像WINDOWS.H和你自己定義它時要carful有多重定義!

14

VC++的行爲不應該對任何有效檢查你可以做。如果你看到0xfeeefeee,那麼你沒有寫入內存(或已經釋放它),所以你不應該從內存中讀取任何。

6

如果你建立在發佈模式而不是調試模式下,那麼運行時不會填滿你根本沒有初始化內存,但它仍然不是零。但是,您應該而不是取決於此行爲 - 您應該使用memset(),ZeroMemory()或SecureZeroMemory()自己顯式初始化內存,或者在某處設置標誌以指示內存尚未初始化。讀取未初始化的內存將導致未定義的行爲。

8

如果您正在閱讀未初始化的內存,您的支票肯定不是「有效」的。內存被釋放。它可能已經在用於別的東西了。你不能對C/C++中未初始化內存的內容做任何假設。我認爲Java(和C#)會保證在分配內存在使用前歸零,當然垃圾回收也會阻止你看到釋放的內存。但這不是C堆的屬性,它直接暴露內存。

4

那其實是一個VC一個非常不錯的功能++(我相信其他的編譯器),因爲它可以讓你看到未分配的內存在調試器的指針。在禁用該功能之前,我會考慮三次。在C++中刪除對象時,應該將指針設置爲NULL以防萬一以後再次嘗試刪除該對象。此功能可以讓您發現忘記將指針設置爲NULL的地方。

0

爲什麼不創建自己的#define並養成使用它的習慣?

I.e.

#define SafeDelete(mem) { delete mem; mem = NULL; } 
#define SafeDeleteArray(mem) { delete [] mem; mem = NULL; }

很明顯,你可以任意命名。 deleteZ,deletesafe,無論你喜歡什麼。

5

你說:

我創建和初始化變量(通過新的),並且一切順利就好了。當我釋放它(通過刪除)時,它將指針設置爲0xFEEEFEEE而不是NULL。當我爲NULL插入適當的檢查時,由於所有管理自己內存的優秀程序都應該這樣做,因爲0xFEEEFEEE通過NULL檢查沒有問題,所以我提出了一些問題。

即使是MSVC的調試堆程序將不會改變指針你刪除的價值 - 你刪除不會改變(甚至爲NULL)指針的值。這聽起來像是你正在訪問一個屬於你剛剛刪除的對象的指針,這是一個簡單而且簡單的錯誤。

我很確定你要做的只是掩蓋無效的內存訪問。你應該發佈一段代碼來向我們展示真正發生的事情。

4

@Jeff哈伯德(comment):

這其實無意中爲我提供了我需要的解決方案:我可以設置pvData爲NULL的_HOOK_FREE,而不是運行與0xFEEEFEEE問題,爲我的指針地址。

如果這是爲你工作,那麼就意味着,當你爲NULL指針測試你正在閱讀釋放的內存(即,指針本身駐留在你釋放的內存)。

這是一個錯誤。

您使用的'解決方案'只是隱藏,而不是修復bug。當釋放的內存被分配給別的東西時,突然間你會使用錯誤的值作爲指向錯誤事物的指針。

1

@ [傑夫·哈伯德]:

發生了什麼事是一個調試編譯下我的代碼崩潰,但在釋放編譯成功。我在一個調試器下面檢查過它,並且在我對它們調用delete之後,我的指針設置爲0xFEEEFEEE。同樣,發佈時的相同代碼不會崩潰,並且按預期運行。

這是非常奇怪的行爲 - 我仍然相信_CrtSetAllocHook()解決方法可能隱藏了一個潛在的錯誤。

0xFEEEFEEE簽名由OS堆管理器用於指示釋放的內存(請參閱http://www.nobugs.org/developer/win32/debug_crt_heap.html)。通過任何機會,你可以發佈一些repro代碼並準確指出你正在使用哪個編譯器版本?

4

如果它在發佈模式下工作,那是因爲剪切運氣。

Mike B認爲調試修復隱藏了一個錯誤是正確的。在釋放模式下,正在使用已釋放但未設置爲NULL的指針,並且指向的內存仍爲「有效」。在未來的某個時間點,內存分配將會改變,或者內存映像將會改變,否則會導致「有效」內存塊變爲「無效」。在這一點上,你的發佈版本將開始失敗。切換到調試模式以查找問題將毫無用處,因爲調試模式已被「固定」。

我認爲我們所有人都同意下面的代碼不應該工作。

char * p = new char[16];  // 16 bytes of random trash 
strcpy(p, "StackOverflow"); // 13 characters, a '\0' terminator, and two bytes of trash 
delete [] p;     // return 16 bytes to the heap, but nothing else changes; 

if (p != NULL)    // Why would p be NULL? It was never set to NULL 
    ASSERT(p[0] == 'S');  // In debug, this will crash, because p = 0xfeeefeee and 
          // dereferencing it will cause an error. 
          // Release mode may or may or may not work, depending on 
          // other memory operations 

由於幾乎所有其他的海報說,指針應該調用delete後設置爲NULL。無論你自己做,還是使用boost或其他包裝,甚至是這個線程中的宏,都取決於你。

3

發生了什麼事是我的代碼崩潰 調試編譯下,但 下釋放編譯成功。

發佈版本會在客戶機器上崩潰。它總是這樣。

我已經在調試器下檢查,並 我的指針越來越設置爲 0xFEEEFEEE後,我呼籲 刪除它們。

指針在你調用delete之後沒有改變。這是他們指向的記憶被設置爲0xfeeefeee,0xfeeefeee,...,0xfeeefeee。

如果你發現你的程序從釋放的內存(這可方便地通過0xfeeefeee模式在調試生成指示)讀取數據時,你有一個錯誤。

0

您還可以創建一個內存管理器。然後你可以覆蓋新的和刪除來從/放回預先分配的內存塊。