C C 執行時庫堆記憶體分析

2021-10-01 19:50:28 字數 3912 閱讀 4552

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);釋放記憶體。

預設情況下,mallocfree都使用_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...