著作權,轉載請註明出處,謝謝!
http://blog.csdn.net/walkinginthewind/article/details/7069176
我們都知道,C語言中要動態申請記憶體需要調用malloc函數,釋放動態記憶體需要調用free函數。記憶體的申請與釋放都是在堆(Heap)上進行的。當然,所謂的記憶體,都是虛擬記憶體。
C語言中的malloc和free,在windows中主要是通過HeapAlloc和HeapFree來實現的。
每個進程在初始化的時候,會調用RtlProcessHeap()函數構造進程的HEAP對象,這個對象用來管理進程的堆記憶體。
當我們使用malloc申請一段記憶體時,我們要指定大小,但是使用free釋放的時候,只是指定要釋放的記憶體起始地址即可。
如:
int * p = (int*)malloc(100 * sizeof(int)); // 申請100個int大小的一段記憶體
... // 其他動作
free(p); // 釋放p所指向的記憶體
那麼我們必定會產生疑問,系統是怎麼知道或記錄給定指標所指向的動態記憶體的大小的呢?
windows的實現方案很簡單,就是在每一段動態記憶體的上部儲存該段記憶體的大小等相關資訊,這也就說明了,當我們使用堆記憶體時,會有額外的系統開銷,windows中是通過如下一個結構體來儲存相關資訊的:
(WRK中的定義,在XP和win7下測試符合,win2000源碼中的定義與此不同)
typedef struct _RTL_HEAP_ENTRY { SIZE_T Size; // 指示該段記憶體的大小 USHORT Flags; USHORT AllocatorBackTraceIndex; union { struct { SIZE_T Settable; ULONG Tag; } s1; // All other heap entries struct { SIZE_T CommittedSize; PVOID FirstBlock; } s2; // RTL_SEGMENT } u;} RTL_HEAP_ENTRY, *PRTL_HEAP_ENTRY;
因為free釋放記憶體的時候要依賴於該結構體中的資訊,所以對於不是malloc返回的地址或已被釋放過的地址,該結構的內容一般是不正確的,這也就是free會出錯的原因。
如:
int * p = (int*)malloc(100 * sizeof(int)); // 申請100個int大小的一段記憶體
... // 其他動作
free(p); // 釋放p所指向的記憶體
free(p); // 再次釋放已釋放的記憶體,會出錯,因為RTL_HEAP_ENTRY已經不再有效。
所以當我們釋放指標所指的動態記憶體後,我們最好將指標賦值為NULL,因為free一個值為NULL的地址,不會有任何錯誤。
再如:
int * p = (int*)malloc(100 * sizeof(int)); // 申請100個int大小的一段記憶體
... // 其他動作
//比如我們想釋放部分記憶體
int * q = p + 20;
free(q); // 釋放q所指向的記憶體,會出錯,因為地址q所對應的RTL_HEAP_ENTRY結構體資訊是無效的
所以free只能釋放malloc返回的有效地址。
下面通過一個程式驗證一下:
#include<stdio.h>#include<windows.h>typedef struct _RTL_HEAP_ENTRY { SIZE_T Size; USHORT Flags; USHORT AllocatorBackTraceIndex; union { struct { SIZE_T Settable; ULONG Tag; } s1; struct { SIZE_T CommittedSize; PVOID FirstBlock; } s2; } u;} RTL_HEAP_ENTRY, *PRTL_HEAP_ENTRY;int main(){PRTL_HEAP_ENTRY pHeapEntry;int *p;for(int i = 0; i < 1000; i++){p=(int*)malloc(i);pHeapEntry=(PRTL_HEAP_ENTRY(p)-1);printf("i: %d, size: %d\n", i, pHeapEntry->Size);free(p);}return 0;}
輸出結果是:
i: 0, size: 0
i: 1, size: 1
i: 2, size: 2
...
i: 999, size: 999
總結:本文根據windows源碼簡單的分析了windows對C庫函數free的實現中對記憶體大小資訊的記錄方法。