1、 系統、開發工具環境:
所有測都是使用的vs2010版本。作業系統為windows xp。
2、 概況
在C語言中,可以配置設定内空間的函數有malloc、calloc、realloc。釋放函數free。關于C++裡面的new和delete,他們兩個跟c很大不同。New和delete是C++運算符。New建立的是一個對象,對象要求對象在建立的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。Malloc和free是辦不到的。但是new和delete各自都調用了malloc和free。是以重還是malloc和free。
要使用malloc函數,可以用stdlib.h或者malloc.h。
Malloc在malloc.h定義如下:
void * __cdecl malloc(_In_ size_t _Size);
我們比較熟悉的就是
void *malloc(unsigned int num_bytes);
num_bytes是訓示的要配置設定的空間的大小。傳回值是void類型。是以我們要強制性轉換成需要的類型。
如果配置設定成功則傳回指向被配置設定記憶體的指針(此存儲區中的初始值不确定),否則傳回空指針NULL
3、 malloc free基本思想
malloc函數有一個将可用的記憶體塊連接配接為一個長長的清單的所謂堆空閑連結清單(或者笛卡爾樹或者記憶體桶)。調用malloc函數時,它沿連接配接表尋找一個大到足以滿足使用者請求所需要的記憶體塊。然後,将該記憶體塊一分為二(一塊的大小與使用者請求的大小相等,另一塊的大小就是剩下的位元組)。接下來,将配置設定給使用者的那塊記憶體傳給使用者,并将剩下的那塊(如果有的話)傳回到空閑連結清單合适的位置上。當沒有足夠大的記憶體時候,就要将小的記憶體的合并成一個大的記憶體(合并的時候一定要是幾個記憶體塊相鄰,不然怎麼合并?)
Free跟malloc相反,當free掉一個空間之後,就會将相應的記憶體放回到連結清單合适的位置上。
通過空閑連結清單,我們可以很輕松進行管理。
Malloc配置設定的記憶體都在堆上。
malloc配置設定結構
比如下面一段代碼:
void main()
{
char*p=(char *)malloc(sizeof(char) * 10);
memcpy(p,"Ilove you1",11);
printf("%s\n",p);
free(p);
}
給p配置設定了10個大小。Free隻有一個函數,那麼free的時候,怎麼知道該怎麼回收記憶體呢?根據的隻是一個p指針嗎?還有,malloc有越界檢查嗎?一切看起來都感覺隻是一個10個大小的空間,怎麼能勝任這麼多要求。
在有的free源碼裡面,有這麼一段話:
void free(void *ptr){
structmem_control_block *free;
free = ptr - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}
Free居然是ptr減去了一個struct結構的大小,然後設定了一個标志。我們不得不有理由相信,malloc配置設定空間的時候,一定還配置設定了一些空間來當做控制資訊,或者說是一個header。實際情況的确如此。可以簡單概括成32+size+4的結構(目前測試是這個結構,可能各個系統不一樣。)
我們就按照第一段代碼調試free,看看具體情況。
設定斷點,先看看配置設定的空間,到free之後,F11進行調試,檢視記憶體。
p指向的記憶體空間是cd,沒有初始化。注意後面的4個fd。
當memcpy之後,p記憶體值變化了。
Free時候F11調試,進去,到memeset這個位置,記憶體還是好好的,注意看上面有一段話:
fill theentire block including header with dead-land-fill
這說明,的确,malloc配置設定的時候,有一個頭。接着下一步。
橢圓的是我們p記憶體的位置,看矩形,說明memset的是這個矩形紅色的内容。數了一下,前面32個位元組,中間10個位元組,後面4個位元組。
這足以說明,配置設定空間是有一個頭,有一個尾部。尾部4個位元組是用來free的時候檢查,看是否越界。比如,你配置設定了10個空間,但是你memcpy了11個,這個時候free就會出問題。
這裡給出malloc配置設定的頭部結構:
typedef struct_CrtMemBlockHeader
{ // Pointer tothe block allocated just before this one: struct _CrtMemBlockHeader*pBlockHeaderNext;
// Pointer to the block allocated just afterthis one:
struct_CrtMemBlockHeader *pBlockHeaderPrev;
char *szFileName; // File name
int nLine; //Line number
size_t nDataSize;// Size of user block
int nBlockUse; //Type of block
long lRequest; //Allocation number
// Buffer justbefore (lower than) the user's memory:
unsigned chargap[nNoMansLandSize];
}_CrtMemBlockHeader;
是以free是根據這個頭部來決定怎麼free的。
4、 realloc
這個函數是來增加大小的,如果該存儲區後有足夠的空間可供擴充,則可在原存儲區位置上向高位址方向擴充,并傳回傳送給它的同樣的指針值。(這樣情況是最好的)如果原存儲區後沒有足夠的空間,則realloc配置設定另一個足夠大的存儲區,将現存内容複制到新配置設定的存儲區。若新記憶體位址原來一樣,就會free掉原來的位址,否則,不會。
Realloc原型:
void *realloc(void *mem_address, unsignedint newsize);
newsize 是新的大小。Newsize要求要大于原來的大小,但是如果小于的話,也沒有關系,但是可能會丢失資料。
例子如下:
void main()
{
char*p=(char *)malloc(sizeof(char) * 10);
memcpy(p,"Ilove you",10);
p = (char*)realloc(p,sizeof(char)*90);
printf("%s\n",p);
free(p);
}
注意p的位址,
同樣的位址已經變成随意内容,說明realloc重新配置設定了空間,原空間已經free了。檢視p的記憶體,如下:
注意,realloc之後我們還是用的p,如果換成其他的比如q指針,p指針是不會消除的,還是存在着。除非主動free。
關于realloc總結如下:
1. realloc失敗的時候,傳回NULL ,realloc之後一定要檢查是否配置設定成功
2. realloc失敗的時候,原來的記憶體不改變,不會釋放也不會移動
3. 假如原來的記憶體後面還有足夠多剩餘記憶體的話,realloc的記憶體=原來的記憶體+剩餘記憶體,realloc還是傳回原來記憶體的位址; 假如原來的記憶體後面沒有足夠多剩餘記憶體的話,realloc将申請新的記憶體,然後把原來的記憶體資料拷貝到新記憶體裡,原來的記憶體将被free掉,realloc傳回新記憶體的位址 (前提是指針是同一個)
4. 如果size為0,效果等同于free()。這裡需要注意的是隻對指針本身進行釋放,例如對二維指針**a,對a調用realloc時隻會釋放一維,使用時謹防記憶體洩露。
5. 傳遞給realloc的指針必須是先前通過malloc(), calloc(), 或realloc()配置設定的
6.傳遞給realloc的指針可以為空,等同于malloc。
7、新配置設定的空間一定要大于原來的空間。小于源大小的話,相當于就縮小了空間,也是可以配置設定的。但是資料的話就可能丢失。
5、 calloc
函數原型:
void *calloc(size_t nobj, size_t size)
該函數相當于malloc+memset
Malloc calloc 都是在heap上配置設定空間。最大的差別就是calloc是會初始化記憶體空間的,malloc不會,需要自己調用memset去初始化。次要差別可以從參數看的出來,calloc傳回的某種對象組成的數組。是以一些程式員在給數組配置設定空間的時候,願意使用calloc。但是其實這兩個差別不打了,malloc申請空間也可以當成數組使用。