3. _crtheap堆
4. 靜態crt庫帶來的影響
5. 總結
在c/c++語言中,我們知道記憶體分為這幾種:
程式全域性變數記憶體。
棧記憶體。
堆記憶體。
其中堆記憶體就是通過malloc(new)來分配的記憶體,本文我們來**一下堆記憶體的分配過程。
對記憶體的使用非常簡單,如下就可以正常使用:
int
* p =
(int*)
malloc
(sizeof
(int))
;*p =
100;
free
(p);
malloc
就是從堆上面分配記憶體,我們看下這個分配記憶體的原理。
void
* __cdecl _nh_malloc (
size_t size,
int nhflag
)}void
* __cdecl malloc (
size_t size
)
分配記憶體的時候,是從_crtheap
堆中分配指定大小的記憶體(heapalloc( _crtheap, 0, size )
)。
void __cdecl free (
void
* pblock
)
釋放記憶體的時候,使用heapfree(_crtheap, 0, pblock);
釋放記憶體。
預設情況下,malloc
,free
都使用_crtheap
堆,這個堆的操作函式如下:
int __cdecl _heap_init (
void
)void __cdecl _heap_term (
void
)
這個**在crt執行時庫下面,也就是說,當這個crt執行時庫載入的時候,就會呼叫_heap_init
,例如乙個dll載入的時候,就會初始化crt執行時庫
0
:000
> kb
# childebp retaddr args to child
00010f
4da4 75a5feb5 00001002
00000000
00000000 ntdll!rtlcreateheap
01010f
4dc8 75c15371 00000000
00001000
00000000 kernelbase!heapcreate+
0x45
02010f
4ddc 75c05ffa 00000001
85cdc24e 75c05b00 msvcrt!_heap_init+
0x1b
03010f
4f28
75c05b29 010f
4f48
010f
4f54
77501d36 msvcrt!_core_crt_dll_init+
0xa7
04010f
4f34
77501d36 75bd0000 00000001
00000000 msvcrt!__crtdll_init+
0x13
05010f
4f54
774c5558 75c05b00 75bd0000 00000001 ntdll!ldrxcallinitroutine+
0x16
06010f
4fa0 774d3edf 00000001
00000000 bdf09388 ntdll!ldrpcallinitroutine+
0x51
有時候,為了減少可執行檔案帶來的crt執行時庫依賴,我們直接將可執行的crt庫鏈結為靜態庫,這樣的話,對於這個可執行檔案的載入,都會執行msvcrt!_heap_init
來初始化執行時庫的堆,很多情況下,這個是可以正常使用的,但是對於如下這種情況,將會帶來問題,而且這種問題不是很容易察覺。
例如場景:
乙個可執行的exe直接執行。
執行的過程載入乙個dll,並呼叫dll的匯出介面。
這個是乙個非常場景的功能,對於windows下面的所有程式,都依賴dll,所以上述操作普通到所有可行性檔案的執行流程。
現在我們假設dll的**如下(為了簡單,這裡只呼叫釋放記憶體):
extern_c
void
__declspec
(dllexport)
myfree
( lpvoid buffer
)
在exe中,我們**如下(分配記憶體,然後提交給dll釋放)
int
freetest()
freelibrary
(hdll);}
std::cout <<
"hello world"
<< std::endl;
return0;
}
我們執行乙個這個**,預設情況下:
hello world
執行正常。
但是如果我們設定一下dlltest.dll
為multi-threaded debug (/mtd),再次執行
出現堆崩潰了。
這個是什麼原因呢?因為:
exe中有乙個crt的堆(_crtheap
·)。
由於dll是靜態鏈結crt庫,所以dll中也需要自己的crt的堆(_crtheap
·)。
pvoid buffer = malloc(1000);
在exe的堆中分配記憶體。
free(buffer);
從dll的堆中釋放記憶體。
釋放過程由於堆不匹配,直接崩潰。
例如針對:pvoid buffer = malloc(1000);
和free(buffer)
反彙編**為:
這裡的malloc
是來自執行時庫ucrtbased!malloc
的。
這裡的釋放介面來自dlltest!free
靜態鏈結到本可執行檔案中了。
從兩個對比圖可以看到,兩個呼叫操作不同的,所以裡面的全域性變數肯定不同 ,_crtheap
也就不同了。
如果是動態鏈結crt,exe和dll都使用同乙個crt執行時dll,所以堆使用的也是同乙個_crtheap
,可以正常執行。
對於上述的例子,你獲取會覺得很扯淡,因為我根本不會在我的乙個模組中(dll或者exe)中分配記憶體,然後在另外乙個模組中釋放。
在乙個模組中呼叫另外乙個模組的介面。
傳遞stl物件作為引數。
這種情況就很可能會出現問題了,因為stl底層會釋放或者分配記憶體,而且很多人會忽略這種情況。
因此,凡是涉及到在乙個模組中分配記憶體,在另外乙個模組中釋放記憶體的操作要盡量避免。
C C 執行時庫 解釋
i.crt crt c c runtime library 是支援c c 執行的一系列函式和 的總稱。雖然沒有乙個很精確的定義,但是可以知道,你的main就是它負責呼叫的,你平時呼叫的諸如strlen strtok time atoi之類的函式也是它提供的。我們以microsoft visual.n...
理解C C 執行時庫
執行時庫 runtime library 通俗的說就是我們的程式執行的時候所依賴的庫檔案,在windows平台這些庫由微軟提供,並且是以2種形式提供 靜態庫 lib 動態庫 lib dll 每個庫還都提供debug release2個版本。c c 執行時庫從形式上來講和我們自己開發的靜態庫 動態庫沒...
C C 執行時的種類
一 c c 執行時的種類 vc 完美的支援c和c 標準,因此也就按照c和c 標準定義的函式原型實現了上述執行時庫。為了方便有不同需求的客戶使用,vc 分別實現了動態鏈結庫dll版本和靜態鏈結庫lib版本。同時為了支援程式除錯且不影響程式的效能,又分別提供了對應的除錯版本。除錯版本的名稱在releas...