轉(zhuǎn):http://www.cnblogs.com/minggoddess/archive/2010/12/15/1907179.html
動(dòng)態(tài)鏈接庫(kù)中分配內(nèi)存引起的
本文主要是探討關(guān)于在動(dòng)態(tài)鏈接庫(kù)分配的內(nèi)存在主程序中釋放所產(chǎn)生的問題,該問題是我在剛做的PJP工程中所遇到的,由于剛碰到之時(shí)感動(dòng)比較詭異(這也是學(xué)識(shí)不夠所致),所以將它寫下來,大家一起分享.
問題來由:
由于該工程中要用到聲音,所以我的分工之一就是用DirectMusic和DirectSound來開發(fā)聲音播放的動(dòng)態(tài)庫(kù),以提供給該工程的兩個(gè)部分:仿真控制部分( 語(yǔ)音 )和三維部分( 場(chǎng)景聲音 )使用,兩個(gè)工程中的聲音都以單獨(dú)的線程播放,且兩個(gè)線程幾乎相同.,然而該動(dòng)態(tài)庫(kù)在以前的運(yùn)行中一直沒有出現(xiàn)過問題, 直到工程開發(fā)即將要結(jié)束階段的前一個(gè)星期,我碰才到了這個(gè)問題,首先是三維部分中聲音在第一次播放是沒有問題,在播放第二個(gè)聲音就出了問題(老是這樣),但是仿真控制部分還是沒有出現(xiàn)問題,我查不出錯(cuò)誤,就用前一天的三維的版本來運(yùn)行,重新生成后聲音播放沒有問題,初步以為是我同事當(dāng)天寫的代碼有問題,可是第二天,同樣的問題又發(fā)生了,還是那樣,三維有問題,仿真部分沒問題,怎么回事呢?我將三維的聲音線程寫成和仿真部分一樣的,還是三維出問題,仿真沒問題.我不相信是動(dòng)態(tài)庫(kù)中出問題,同一個(gè)動(dòng)態(tài)庫(kù),同樣的代碼,只是在兩個(gè)不同的程序里,為什么一個(gè)出問題,而另一個(gè)不出問題呢?
出錯(cuò)信息如下:
windows已在PowipD.exe中觸發(fā)一個(gè)斷點(diǎn).
其原因可能是堆被損壞,這也說明PowipD.exe中或它所加載的任何DLL中有bug.
輸出窗口可能提供了更多診斷信息
按下中斷后輸入窗口出現(xiàn)的信息如下:
HEAP[PowipD.exe]:Invalid Address specified to RtlValidateHeap( 01CC0000, 03723758 )
Windows已在PowipD.exe中觸發(fā)一個(gè)斷點(diǎn).
輸出窗口可能提供了更多診斷信息
由于這些原因讓我迷惑不解,到底是為什么出現(xiàn)這種奇怪的事情呢?問題出在那里?
問題的解決:
簡(jiǎn)單地說:DLL中分配的內(nèi)存DLL要負(fù)責(zé)釋放?。ㄒ粋€(gè)模塊分配的內(nèi)存要在同一個(gè)模塊中釋放?。?/p>
找到解決方法后我查看了三個(gè)工程的設(shè)置(三個(gè)工程皆用VS2005開發(fā)):
三維:使用MDd(多線程調(diào)試DLL)運(yùn)行期庫(kù)
仿真控制:使用MTd(多線程調(diào)試)運(yùn)行期庫(kù)
聲音動(dòng)態(tài)庫(kù):使用MDd(多線程調(diào)試DLL)運(yùn)行期庫(kù)
于是,我將三個(gè)工程皆改為:使用MDd(多線程調(diào)試DLL)運(yùn)行期庫(kù)時(shí),問題解決。
但是如果三個(gè)工程中有的不是用C/C++寫的,或者其中有工程的設(shè)置已經(jīng)不便更改了,那又有什么辦法呢?
該問題主要是關(guān)于DLL與進(jìn)程的地址空間的問題(下面是<<核心編程>>中的一段話):
單個(gè)地址空間是由一個(gè)可執(zhí)行模塊和若干個(gè)DLL模塊組成,這些模塊中,有些可以鏈接到靜態(tài)版本的C/C++運(yùn)行期庫(kù), 有些可以鏈接到一個(gè)DLL版本的運(yùn)行期庫(kù),而有些模塊(如果不是用C/C++編寫的話)則根本不需要C/C++運(yùn)行期庫(kù),許多開發(fā)人員經(jīng)常會(huì)犯一個(gè)常見的錯(cuò)誤,因?yàn)樗麄兺浟巳舾蓚€(gè)C/C++運(yùn)行期庫(kù)可以存在于單個(gè)地址空間中.請(qǐng)看下面的代碼(下面代碼不是書上的):
DLL中如下:
int *DllFunc()
{
int *p = new int;
return p;
}
EXE中如下:
void EXEFunc()
{
int *p = DllFunc();
if( p!= NULL )
delete p;
}
如何看待這個(gè)問題呢?上面的代碼能夠正確運(yùn)行嗎?DLL函數(shù)分配的內(nèi)存是由EXE的函數(shù)釋放的嗎?答案是可能的.上面顯示的代碼并沒有提供足夠的信息.如果EXE和DLL都鏈接到DLL的C/C++運(yùn)行期庫(kù),那么上面的代碼將能夠很好地運(yùn)行.但是,如果兩個(gè)模塊中的一個(gè)或者兩個(gè)鏈接到靜態(tài)C/C++運(yùn)行期庫(kù),那個(gè)對(duì)delete的操作就會(huì)失敗.
一個(gè)很方便的方法可以解決這個(gè)問題.當(dāng)一個(gè)模塊提供一個(gè)用于分配內(nèi)存塊的函數(shù)時(shí),該模塊也必須提供相應(yīng)的釋放內(nèi)存的函數(shù),將上面的代碼改成如下的樣子就不會(huì)出錯(cuò)了:
DLL中如下:
int *DllFunc()
{
int *p = new int;
return p;
}
void DLLDelete( int *p)
{
if( p != NULL )
delete p;
}
EXE中如下:
void EXEFunc()
{
int *p = DllFunc();
DLLDelete( p );
}
這樣的代碼才是正確的,當(dāng)我將代碼改成如上類似后,再將三維的運(yùn)行期庫(kù)改為原來一樣(使用MDd(多線程調(diào)試DLL)運(yùn)行期庫(kù)),三維的聲音也正確無(wú)誤地播放出來了。當(dāng)你在編寫一個(gè)模塊時(shí),你時(shí)刻都得記著,別人的代碼不一定是用C/C++寫的,也有可能不能同時(shí)鏈接到DLL的C/C++運(yùn)行期庫(kù)。malloc和free函數(shù)也有類似的問題。
當(dāng)然在你在生成你的模塊時(shí)是可以選擇運(yùn)行期庫(kù)的,VC 6.0配有6個(gè)運(yùn)行期庫(kù):其描述如下:
庫(kù)名 |
描述 |
LibC.lib |
用于單線程應(yīng)用程序的靜態(tài)應(yīng)用程序的靜態(tài)鏈接庫(kù) |
LibCD.lib |
用于單線程應(yīng)用程序的靜態(tài)鏈接庫(kù)的調(diào)試版 |
LibCMt.lib |
用于多線程應(yīng)用程序的靜態(tài)鏈接庫(kù)的發(fā)行版 |
LibCMtD.lib |
用于多線程應(yīng)用程序的靜態(tài)鏈接庫(kù)的調(diào)試版 |
MSVCRt.lib |
用于動(dòng)態(tài)鏈接MSVCRt.dll庫(kù)的發(fā)行版的輸入庫(kù) |
MSVCRtD.lib |
用于動(dòng)態(tài)鏈接MSVCRtD.dll的調(diào)試版的輸入庫(kù)。該庫(kù)同時(shí)支持單線程應(yīng)用程序和多線程應(yīng)用程序 |
而VS2005中只配了4個(gè)C/C++運(yùn)行期庫(kù),就是上表中的后面4個(gè)。
建議各位在軟件開發(fā)時(shí)同一軟件的不同模塊最好使用一致的運(yùn)行庫(kù),否則還可能出現(xiàn)鏈接問題。