天天看點

對動态記憶體配置設定函數malloc、calloc、realloc、free的了解

動态記憶體配置設定:

  動态記憶體配置設定就是指在程式執行的過程中動态地配置設定或者回收存儲空間的配置設定記憶體的方法。動态記憶體配置設定不像數組等靜态記憶體配置設定方法那樣需要預先配置設定存儲空間,而是由系統根據程式的需要即時配置設定,且配置設定的大小就是程式要求的大小。

  以一個數組為例:在定義數組時給定了記憶體空間,如果一開始給定的記憶體空間足夠大,就能夠放入需要放入的所有資料元素,但如果給定的記憶體空間小于所需要放入的資料元素個數時,我們就需要給這個記憶體空間擴容,以滿足放入所有資料元素的需求。

  在擴容過程中,我們需要注意以下兩個問題:

  1. 如果增容空間過大而資料元素很少,就會導緻記憶體空間浪費;
  2. 如果增容空間不足而資料元素較多,就會導緻記憶體溢出現象。

動态記憶體配置設定函數有:

  • malloc函數:

  malloc的全稱是memory allocation,中文名為動态記憶體配置設定,用于申請一塊連續的指定大小的記憶體塊區域以void*類型傳回配置設定的記憶體區域位址,當無法知道記憶體具體位置的時候,想要綁定真正的記憶體空間,就需要用到動态的配置設定記憶體,且配置設定的大小就是程式要求的大小。

  函數原型為:void *malloc(unsigned int size);,其作用是在記憶體的動态空間中配置設定一個長度為size的連續空間。此函數的傳回值是配置設定區域的起始位址,或者說,此函數是一個指針型函數,傳回的指針指向該配置設定域的開頭位置。

  如果配置設定成功則傳回指向被配置設定記憶體的指針(此空間中的初始值不确定),否則傳回空指針NULL。當記憶體不再使用時,應使用free()函數将記憶體塊釋放。函數傳回的指針一定要适當對齊,使其可以用于任何資料對象。

  • calloc函數:

  calloc的全稱是clear allocation,中文名為動态記憶體配置設定并清零,···。

  函數原型為:void *calloc(unsigned int num,unsigned int size);,···。

  如果配置設定成功則傳回指向被配置設定記憶體的指針(此空間中的初始值為0),否則傳回空指針NULL,···。

  • realloc函數:

  realloc的全稱是reset allocation,中文名為動态記憶體調整,···。

  函數原型為:extern void *realloc(void *mem_address, unsigned int newsize);,···。

  先判斷目前的指針是否有足夠的連續空間,如果有,擴大mem_address指向的位址,并且将mem_address傳回,如果空間不夠,先按照newsize指定的大小配置設定空間,将原有資料從頭到尾拷貝到新配置設定的記憶體區域,而後釋放原來mem_address所指記憶體區域(原來的指針會自動釋放,不需要再使用free),同時傳回新配置設定的記憶體區域的首位址。重新配置設定成功傳回指向被配置設定記憶體的指針,否則傳回空指針NULL。

  注意:調整後的大小可大可小(如果新的大小大于原記憶體大小,新配置設定部分不會被初始化;如果新的大小小于原記憶體大小,可能會導緻資料丢失)。

  • free函數:

  函數原型為:void free(void *ptr);,一般使用malloc,calloc,realloc函數進行記憶體配置設定後要使用free(起始位址的指針) 對記憶體進行釋放,不然記憶體申請過多會影響計算機的性能,以至于重新開機電腦。但是若使用動态記憶體配置設定函數後未使用free函數進行釋放,還可以使用指針對該塊記憶體進行通路,如果釋放則不能再通路。

  注意:使用後該指針變量一定要重新指向NULL,防止野指針出現,有效規避錯誤操作。

(以上函數都被包含于頭檔案stdlib.h中)

總結:

  1. malloc函數可以配置設定指定位元組數的空間,但該空間中的初始值不确定
  2. calloc函數可以配置設定指定長度的空間,該空間中的初始值都被初始化為0
  3. realloc函數可以更改以前空間的長度(可以增加也可以減少)
malloc與calloc的差別:

  對于malloc函數,其原型為void *malloc(unsigned int size);,隻有一個參數,size為我們要申請的空間大小,需要手動計算,比如我們要申請10個int類型的空間:

int *p = (int*)malloc(10 * sizeof(int))

,如果編譯器預設int為4個位元組進行存儲的話,那麼計算結果為40byte,即申請了一個40byte的連續空間,并強制轉換為int類型,指派給指針p,但此時申請到空間裡的初始值是不确定的。

  而對于calloc函數,其原型為void *calloc(unsigned int num,unsigned int size);,比malloc函數多一個參數num,是以它不需要人為計算空間大小,比如我們要申請10個int類型的空間:

int *pp = (int*)calloc(10, sizeof(int))

,這樣寫就能省去人為空間計算這一步驟。

  但是,是否省去人為空間計算并不是他們最主要的差別,最主要的差別是malloc申請空間後空間裡的值是随機的,并沒有進行初始化,而calloc在申請空間後會對空間内的資料進行逐一初始化,并将初始值設定為0。

  • 代碼如下:
#include "stdio.h"
#include "malloc.h"
int main()
{
	int i;
	int *p = (int*)malloc(10 * sizeof(int));
	int *pp = (int*)calloc(10, sizeof(int));
	printf("malloc申請的空間值:\n");
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", p[i]);
	}
	printf("\n");
	printf("calloc申請的空間值:\n");
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", pp[i]);
	}
	free(p);
	free(pp);
	return 0;
}
           
  • 運作如下:
對動态記憶體配置設定函數malloc、calloc、realloc、free的了解

  既然calloc函數不需要人為計算空間大小,并且可以直接将記憶體空間的資料初始化為0進而避免錯誤,那為什麼不直接使用calloc函數?malloc函數存在的意義又是什麼?

  我們知道,事物都具有兩面性。對于這兩個函數來說,由于calloc函數要将空間内的每一個資料都初始化為0,那麼運作效率較malloc函數必然會大大降低,實際過程中大多數情況的空間申請也是不需要對資料進行初始化的,這也就是malloc函數存在的意義。

realloc與malloc及calloc的差別

  說完了malloc函數和calloc函數,接下來談談realloc函數。realloc函數和上面兩個函數有本質差別,其原型為extern void *realloc(void *mem_address, unsigned int newsize);,用于對動态記憶體進行擴容,即已申請的動态空間不夠用,其中mem_address為指向原空間位址的指針,newsize為接下來擴充的容量大小。

  • 代碼如下:

#include "stdio.h"
#include "malloc.h"
int main()
{
	const int size = 100;
	int *p = (int*)malloc(10 * sizeof(int));
	int *pp = (int*)realloc(p, size * sizeof(int));
	printf("原Address:%d\n新Address:%d\n", p, pp);
	free(pp);
	return 0;
}
           
  • 運作如下:

對動态記憶體配置設定函數malloc、calloc、realloc、free的了解

  可從圖看出,擴容後位址和原先位址是不一樣的,這取決于擴容的記憶體大小。如果size較小,原來申請的動态記憶體後面還有空餘記憶體,系統将直接在原記憶體空間後面擴容,并傳回原動态空間的位址;如果size較大,原來申請的空間後面沒有足夠大的空間擴容,系統将重新申請一塊(10+size)*sizeof(int)的記憶體,并把原來空間的内容拷貝過去,原空間free;如果size非常大,則記憶體空間申請失敗,傳回空指針NULL,原來的記憶體不會釋放。

  注意:如果擴容後的記憶體空間較原空間小,将會出現資料丢失,如果直接realloc(p, 0),相當于free。

繼續閱讀