1. 程式人生 > >malloc/free的用法詳解 (轉)

malloc/free的用法詳解 (轉)

原文章:http://blog.chinaunix.net/uid-21411227-id-1826820.html

C裡,記憶體管理是通過專門的函式來實現。另外,為了相容各種程式語言,作業系統提供的介面通常是語言寫成的函式宣告(Windows 本身也由C和組合語言寫成)。

分配記憶體 malloc 函式

需要包含標頭檔案:

     and  

函式宣告(函式原型)

void *malloc(int size);

說明:malloc 向系統申請分配指定size個位元組的記憶體空間。返回型別是 void* 型別。void* 表示未確定型別的指標。C,C++規定,void* 型別可以強制轉換為任何其它型別的指標。

從函式宣告上可以看出。malloc  new 至少有兩個不同new 返回指定型別的指標,並且可以自動計算所需要大小。比如:

int *p;

p = new int; //返回型別為int* 型別(整數型指標),分配大小為 sizeof(int);

或: 

int* parr;

parr = new int [100];  //返回型別為 int* 型別(整數型指標),分配大小為 sizeof(int) * 100; 

 malloc 則必須由我們計算要位元組數,並且在返回後強行轉換為實際型別的指標。 

int* p; 

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

第一、malloc 

函式返回的是 void * 型別,如果你寫成:p = malloc (sizeof(int)); 則程式無法通過編譯,報錯:“不能將 void* 賦值給 int * 型別變數”。所以必須通過 (int *) 來將強制轉換。

第二、函式的實參為 sizeof(int) ,用於指明一個整型資料需要的大小。如果你寫成: 

int* p = (int *) malloc (1);

程式碼也能通過編譯,但事實上只分配了1個位元組大小的記憶體空間,當你往裡頭存入一個整數,就會有3個位元組無家可歸,而直接“住進鄰居家”!造成的結果是後面的記憶體中原有資料內容全部被清空。

malloc 也可以達到 new [] 

的效果,申請出一段連續的記憶體,方法無非是指定你所需要記憶體大小。

比如想分配100int型別的空間:

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100個整數的記憶體空間。

另外有一點不能直接看出的區別是,malloc 只管分配記憶體,並不能對所得的記憶體進行初始化,所以得到的一片新記憶體中,其值將是隨機的。

除了分配及最後釋放的方法不一樣以外,通過mallocnew得到指標,在其它操作上保持一致。

釋放記憶體 free 函式

需要包含標頭檔案( malloc 一樣)

函式宣告:

void free(void *block);

即: void free(指標變數)

之所以把形參中的指標宣告為 void* ,是因為free必須可以釋放任意型別的指標,而任意型別的指標都可以轉換為void *

舉例:

int* p = (int *) malloc(4); 

*p = 100;

free(p); //釋放 p 所指的記憶體空間

或者:

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100個整數的記憶體空間。

……

free(p);

free 不管你的指標指向多大的空間,均可以正確地進行釋放,這一點釋放比 delete/delete [] 要方便。不過,必須注意,如果你在分配指標時,用的是newnew[],那麼抱歉,當你在釋放記憶體時,你並不能圖方便而使用free來釋放。反過來,你用malloc 分配的記憶體,也不能用delete/delete[] 來釋放。一句話,new/deletenew[]/delete[]malloc/free 三對均需配套使用,不可混用!

3.calloc()realloc()

calloc()函式有兩個引數,分別為元素的數目和每個元素的大小,這兩個引數的乘積就是要分配的記憶體空間的大小:void *calloc(size_t numElements,size_t sizeOfElement); 

如果呼叫成功,函式malloc()和函式calloc()都將返回所分配的記憶體空間的首地址。函式malloc()和函式calloc()的主要區別是前者不能初始化所分配的記憶體空間,而後者能。如果由malloc()函式分配的記憶體空間原來沒有被使用過,則其中的每一位可能都是0;反之,如果這部分記憶體曾經被分配過,則其中可能遺留有各種各樣的資料。也就是說,使用malloc()函式的程式開始時(記憶體空間還沒有被重新分配)能正常進行,但經過一段時間(記憶體空間還已經被重新分配)可能會出現問題。

函式calloc()會將所分配的記憶體空間中的每一位都初始化為零,也就是說,如果你是為字元型別或整數型別的元素分配記憶體,那麼這些元素將保證會被初始化為0;如果你是為指標型別的元素分配記憶體,那麼這些元素通常會被初始化為空指標;如果你為實型資料分配記憶體,則這些元素會被初始化為浮點型的零。

realloc()

原型:extern void *realloc(void *mem_address, unsigned int newsize);

用法:#include 有些編譯器需要#include

功能:改變mem_address所指記憶體區域的大小為newsize長度。

說明:如果重新分配成功則返回指向被分配記憶體的指標,否則返回空指標NULL

當記憶體不再使用時,應使用free()函式將記憶體塊釋放。

注意:這裡原始記憶體中的資料還是保持不變的。

舉例:

  // realloc.c

  #include

  #include

  main()

  {

  char *p;

  clrscr(); // clear screen

  p=(char *)malloc(100);

  if(p)

  printf("Memory Allocated at: %x",p);

  else

  printf("Not Enough Memory!\n");

  getchar();

  p=(char *)realloc(p,256);

  if(p)

  printf("Memory Reallocated at: %x",p);

  else

  printf("Not Enough Memory!\n");

  free(p);

  getchar();

  return 0;

  }

詳細說明及注意要點:

  1、如果有足夠空間用於擴大mem_address指向的記憶體塊,則分配額外記憶體,並返回mem_address

這裡說的是“擴大”,我們知道,realloc是從堆上分配記憶體的,當擴大一塊記憶體空間時, realloc()試圖直接從堆上現存的資料後面的那些位元組中獲得附加的位元組,如果能夠滿足,自然天下太平。也就是說,如果原先的記憶體大小後面還有足夠的空閒空間用來分配,加上原來的空間大小= newsize。那麼就ok。得到的是一塊連續的記憶體。

  2、如果原先的記憶體大小後面沒有足夠的空閒空間用來分配,那麼從堆中另外找一塊newsize大小的記憶體。

並把原來大小記憶體空間中的內容複製到newsize中。返回新的mem_address指標。(資料被移動了)。

老塊被放回堆上。

例如:

  #include

  char *p*q;

  p = (char * ) malloc (10);

  q=p;

  p = (char * ) realloc (p,20);

  …………………………

這段程式也許在編譯器中沒有辦法通過,因為編譯器可能會為我們消除一些隱患!在這裡我們只是增加了一個記錄原來記憶體地址的指標q,然後記錄了原來的記憶體地址p,如果不幸的話,資料發生了移動,那麼所記錄的原來的記憶體地址q所指向的記憶體空間實際上已經放回到堆上了!這樣一來,我們應該終於意識到問題的所在和可怕了吧!

  3、返回情況

返回的是一個void型別的指標,呼叫成功。(這就再你需要的時候進行強制型別轉換)

返回NULL,當需要擴充套件的大小(第二個引數)為0並且第一個引數不為NULL,此時原記憶體變成了“freed(遊離)”的了。

返回NULL,當沒有足夠的空間可供擴充套件的時候,此時,原記憶體空間的大小維持不變。

  4、特殊情況

如果mem_addressnull,則realloc()malloc()類似。分配一個newsize的記憶體塊,返回一個指向該記憶體塊的指標。

如果newsize大小為0,那麼釋放mem_address指向的記憶體,並返回null

如果沒有足夠可用的記憶體用來完成重新分配(擴大原來的記憶體塊或者分配新的記憶體塊),則返回null.而原來的記憶體塊保持不變。