1. 程式人生 > >VC中“其原因可能是堆被損壞,這也說明*.exe中或它所載入的任何DLL中有bug”的分析

VC中“其原因可能是堆被損壞,這也說明*.exe中或它所載入的任何DLL中有bug”的分析

找了一下午錯誤原因,整理一下大神的資料 原因分析

來自百度空間:DLL和exe裡的malloc和free不能混用[hi.baidu.com/huhejun/item/988e41361d0a95c7392ffab5];

來自CSDN論壇:Dll分配的記憶體塊,應用釋放的問題;

來自CSDN博文:VC執行時庫/MD、/MDd、/MT、/MTd說明 - holybin的專欄。

綜合以上幾個地方的討論,給出一個總結分析:

1、首先是模組和程序的關係。win32環境下,模組分為兩種:程序內模組和程序外模組,前者共享程序的記憶體空間,比如許多在程序中載入的dll;後者與程序一樣,獨立執行,通常供其它程序呼叫(由引用計數之類的管理),程序外模組當然也包括dll。在後面的敘述中簡化成程序(exe)和同等地位的模組(dll)。【至於程序與模組的詳細關係,我整理了一篇:模組與程序的關係 - holybin的專欄】

2、其次是在記憶體使用上,程序有堆,而執行緒有棧:一個程序(模組)一個堆,一個執行緒一個棧。由於一個程序必然有一個主執行緒,所以一個程序(模組)會有一個堆(用於new分配記憶體)和一個棧(用於區域性變數)與之繫結,這兩個的大小都可以通過編譯器選項指定。所以exe和dll應該擁有不同的堆記憶體,使得在其中一個那裡申請的記憶體,不能在另一個那裡釋放(在哪借東西就在哪裡還對吧)。

3、最後最重要的是,C執行時庫(Cruntime library,CRT)是靜態連線的。CRT不是使用程序預設的堆來實現malloc(new中呼叫malloc)的,而是使用一個全域性控制代碼 HANDLE _crtheap來分配記憶體的,這個_crtheap是在XXXCRTStartUp(CRT提供的進口點函式)中建立的。 由於CRT靜態連線,所以dll裡有也會有CRT,因此也會有_crtheap。而在dll中的new(malloc)使用dll中的_crtheap控制代碼分配堆記憶體,在exe中的delete(free)中使用exe中的_crtheap釋放堆記憶體,所以失敗。

解決辦法

1、使用單一的堆:分配記憶體把相應的new type[size]改為 HeapAlloc(GetProcessHeap(),0,size),釋放記憶體時把相應的delete []p 改為HeapFree(GetProcessHeap(),0,p);或者用GlobalAlloc()代替new, 用GlobalFree()代替delete。

2、dll分配的記憶體由dll釋放:在dll中輸出一個函式給exe呼叫,專門用來釋放由dll分配的記憶體。

綜合(1)(2)兩點,記憶體操作時不能混用malloc、new、free、delete等操作符和函式:

C

C++

Windows平臺

COM

IMalloc介面

BSTR

申請

malloc()

new

GlobalAlloc()

CoTaskMemAlloc()

Alloc()

SysAllocString()

重新申請

realloc()

GlobalReAlloc()

CoTaskRealloc()

Realloc()

SysReAllocString()

釋放

free()

delete

GlobalFree()

CoTaskMemFree()

Free()

SysFreeString()

以上這些函式必須要按型別配合使用(比如:new 申請的記憶體,則必須用 delete 釋放;malloc申請的必須用free釋放)。在 COM 內部,當然你可以隨便使用任何型別的記憶體分配釋放函式,但元件如果需要與客戶進行記憶體的互動,則必須使用上表中的後三類函式族。IMalloc 介面又是對CoTaskXXX() 函式族的一個包裝。包裝後,同時增強了一些功能,比如:IMalloc::GetSize()可以取得尺寸,使用 IMallocSpy 可以監視記憶體的使用。

3、最簡單的辦法是修改專案屬性中的“C執行時庫”選項:專案屬性—>配置屬性—>C/C++—>程式碼生成—>執行時庫,將Debug模式下的改成“多執行緒除錯DLL(/MDd)”,或者將Release模式下的改成“多執行緒DLL(/MD)”。這樣exe和dll就是使用同一個CRT(MSVCRT.DLL),就可以直接使用new和delete了。

再次編譯執行時,可能會出現如下錯誤:fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD,表明需要設定共享DLL。

解決方案:“專案屬性”—>“配置屬性”—>“常規”—>“專案預設值”—>“MFC的使用”,選擇“在共享 DLL 中使用 MFC”。

實際測試(出處不明) 測試1:使用malloc/free組合來分配和釋放記憶體,DLL中使用 malloc分配,exe中使用free釋放。

我建的是Win32 DLL工程, C/C++->Code generation 設定是 Multithread DLL debug, 但是exe工程設定是

MultiThread debug,所以不管怎麼樣,總是會拋異常. 這就間接證明了上述的描述是正確的, 若我修改exe工程設定是

MultiThread DLL debug, 那麼malloc/free組合就能很好的工作起來了。

測試2:使用HeapAlloc/HeapFree組合來分配和釋放記憶體,DLL 中使用HeapAlloc分配,exe中釋放。

exe的配置還是MultiThread Debug,DLL中HeapAlloc(GetProcessheap(), HEAP_ZERO_MEMORY, 1024)分配,

exe中HeapFree(GetProcessHeap(), 0, p)釋放,,則還是無法正常執行,還是拋異常。若exe中設定成MultiThread 

DLL debug就正常運行了。

測試3:還是使用HeapAlloc/HeapFree來進行,但是DLL中匯出一個方法來釋放DLL中分配的記憶體。

若exe配置是MultiThread Debug,無法正常執行,拋異常。若修改成MultiThread DLL debug正常執行。

結論如下:

不管是使用malloc/free組合還是HeapAlloc/HeapFree組合,exe工程均需要設定成MultiThread DLL debug才能

正常執行起來的,CSDN上的那個討論在這兒貌似是由出入的,而且DLL的設定不能隨意修改。所以若有涉及到這種

問題的,最好的辦法還是在哪個模組分配的就在哪個模組釋放最好,要不然反倒會引來更多的麻煩。 ---------------------  作者:holybin  來源:CSDN  原文:https://blog.csdn.net/holybin/article/details/26132807  版權宣告:本文為博主原創文章,轉載請附上博文連結!