天天看點

動态記憶體申請函數選擇(realloc、malloc 、alloca、 calloc)

1、函數realloc 

函數簡介:

     原型 :extern void *realloc(void *mem_address, unsigned int newsize);      文法:指針名=(資料類型*)realloc(要改變記憶體大小的指針名,新的大小)。//新的大小一定要大于原來的大小,不然的話會導緻資料丢失!      頭檔案:#include <stdlib.h> 有些編譯器需要#include <malloc.h>,在TC2.0中可以使用alloc.h頭檔案      功能:先判斷目前的指針是否有足夠的連續空間,如果有,擴大mem_address指向的位址,并且将mem_address傳回,如果空間不夠,先按照newsize指定的大小配置設定空間,将原有資料從頭到尾拷貝到新配置設定的記憶體區域,而後釋放原來mem_address所指記憶體區域,同時傳回新配置設定的記憶體區域的首位址。即重新配置設定存儲器塊的位址。     傳回值:如果重新配置設定成功則傳回指向被配置設定記憶體的指針,否則傳回空指針NULL。      注意:這裡原始記憶體中的資料還是保持不變的。當記憶體不再使用時,應使用free()函數将記憶體塊釋放。   詳細說明及注意要點:               1、如果有足夠空間用于擴大mem_address指向的記憶體塊,則配置設定額外記憶體,并傳回mem_address。 這裡說的是“擴大”,我們知道,realloc是從堆上配置設定記憶體的,當擴大一塊記憶體空間時, realloc()試圖直接從堆上現存的資料後面的那些位元組中獲得附加的位元組,如果能夠滿足,自然天下太平。也就是說,如果原先的記憶體大小後面還有足夠的空閑空間用來配置設定,加上原來的空間大小= newsize。那麼就ok。得到的是一塊連續的記憶體。       2、如果原先的記憶體大小後面沒有足夠的空閑空間用來配置設定,那麼從堆中另外找一塊newsize大小的記憶體。 并把原來大小記憶體空間中的内容複制到newsize中。傳回新的mem_address指針。(資料被移動了)。 老塊被放回堆上。     

例如:
#include <malloc.h>
void main()
{
   char *p,*q;
   p = (char * ) malloc (10);
   q=p;
   p = (char * ) realloc (q,20); //A
   …………………………
}
           

     在這段程式中我們增加了指針q,用它記錄了原來的記憶體位址p。這段程式可以編譯通過,但在執行到A行時,如果原有記憶體後面沒有足夠空間将原有空間擴充成一個連續的新大小的話,realloc函數就會以第二種方式配置設定記憶體,此時資料發生了移動,那麼所記錄的原來的記憶體位址q所指向的記憶體空間實際上已經放回到堆上了!這樣就會産生q指針的指針懸挂,即指針指向了一塊沒有配置設定給使用者使用的記憶體,如果再用q指針進行操作就可能發生意想不到的問題。是以在應用realloc函數是應當格外注意這種情況。

     3、傳回情況      傳回的是一個void類型的 指針 :調用成功。(這就要求在你需要的時候進行 強制類型轉換 )      傳回NULL:當需要擴充的大小(第二個參數)為0并且第一個參數不為NULL時。此時原記憶體變成“ free d(遊離)”的了。    傳回NULL:當沒有足夠的空間可供擴充的時候。此時,原記憶體空間的大小維持不變。 4、特殊情況       如果mem_address為NULL,則realloc()和 malloc ()類似。配置設定一個newsize的記憶體塊,傳回一個指向該記憶體塊的 指針 。      如果newsize大小為0,那麼釋放mem_address指向的記憶體,并傳回NULL。      如果沒有足夠可用的記憶體用來完成重新配置設定(擴大原來的記憶體塊或者配置設定新的記憶體塊),則傳回NULL。而原來的記憶體塊保持不變。   realloc使用總結:        1. realloc失敗的時候,傳回NULL     2. realloc失敗的時候,原來的記憶體不改變,不會釋放也不會移動     3 . 假如原來的記憶體後面還有足夠多剩餘記憶體的話,realloc的記憶體=原來的記憶體+剩餘記憶體,realloc還是傳回原來記憶體的位址; 假如原來的記憶體後面沒有足夠多剩餘記憶體的話,realloc将申請新的記憶體,然後把原來的記憶體資料拷貝到新記憶體裡,原來的記憶體将被 free 掉,realloc傳回新記憶體的位址     4. 如果size為0,效果等同于 free ()。這裡需要注意的是隻對 指針 本身進行釋放,例如對二維指針**a,對a調用realloc時隻會釋放一維,使用時謹防 記憶體洩露 。     5. 傳遞給realloc的 指針 必須是先前通過 malloc (), calloc (), 或realloc()配置設定的     6.傳遞給realloc的 指針 可以為空,等同于 malloc 。   2、alloca()函數      函數簡介      記憶體配置設定 函數,與malloc,calloc,realloc類似,但是注意一個重要的差別, _alloca是在棧(stack)上申請空間,用完馬上就釋放,包含在頭檔案malloc.h中,在某些系統中會宏定義成 _alloca使用。     函數原型 void * __cdecl  alloca(size_t);   注意事項        在調用 alloca的函數傳回的時候, 它配置設定的記憶體會自動釋放。也就是說, 用 alloca 配置設定的記憶體在棧上。 alloca不具可移植性, 而且在沒有傳統 堆棧 的機器上很難實作。當它的傳回值直接傳入另一個函數時會帶來問題,因為他配置設定在棧上.由于這些原因, alloca不宜使用在必須廣泛移植的程式中, 不管它可能多麼有用。

int main()
{
    int *p = (int *)alloca(sizeof(int)*10);
    free(p);//此時不能用free()去釋放,會導緻錯誤
     return 0;
}
           

3 、calloc函數     函數簡介          calloc是一個 C語言 函數      函數名: calloc      void *calloc(unsigned n,unsigned size);      功 能: 在記憶體的 動态存儲 區中配置設定n個長度為size的連續空間,函數傳回一個指向配置設定起始位址的 指針 ;如果配置設定不成功,傳回NULL。      跟 malloc 的差別:      calloc在動态配置設定完記憶體後,自動初始化該記憶體空間為零,而 malloc 不初始化,裡邊資料是随機的垃圾資料。并且malloc申請的記憶體可以是不連續的,而calloc申請的記憶體空間必須是連續的。

#include <stdlib.h>
#include<string.h>
#include <stdio.h>
int main(void)
{
    char *str = NULL;
    /* 配置設定記憶體空間 */
    str = (char*)calloc(10, sizeof(char));
    /* 将hello寫入*/
    strcpy(str, "Hello");
    /*顯示變量内容*/
    printf("String is %s\n", str);
    /* 釋放空間 */
    free(str);
    return 0;
}
           

4、malloc函數       函數簡介 原型:extern void *malloc(unsigned int num_bytes);     

    功能:配置設定長度為num_bytes位元組的記憶體塊      傳回值:如果配置設定成功則傳回指向被配置設定記憶體的 指針 (此存儲區中的初始值不确定),否則傳回空指針NULL。當記憶體不再使用時,應使用 free ()函數将記憶體塊釋放。函數傳回的 指針 一定要适當對齊,使其可以用于任何 資料對象 。     說明:關于該函數的原型,在舊的版本中malloc傳回的是char型 指針 ,新的ANSIC标準規定,該函數傳回為void型指針,是以必要時要進行類型轉換。   函數的工作機制        malloc函數的實質展現在,它有一個将可用的記憶體塊連接配接為一個長長的清單的所謂空閑 連結清單 。調用malloc函數時,它沿 連接配接表 尋找一個大到足以滿足使用者請求所需要的記憶體塊。然後,将該記憶體塊一分為二(一塊的大小與使用者請求的大小相等,另一塊的大小就是剩下的位元組)。接下來,将配置設定給使用者的那塊記憶體傳給使用者,并将剩下的那塊(如果有的話)傳回到連接配接表上。調用 free 函數時,它将使用者釋放的記憶體塊連接配接到空閑鍊上。到最後,空閑鍊會被切成很多的小記憶體片段,如果這時使用者申請一個大的記憶體片段,那麼空閑鍊上可能沒有可以滿足使用者要求的片段了。于是,malloc函數請求延時,并開始在空閑鍊上翻箱倒櫃地檢查各記憶體片段,對它們進行整理,将相鄰的小空閑塊合并成較大的記憶體塊。如果無法獲得符合要求的記憶體塊,malloc函數會傳回NULL 指針 ,是以在調用malloc動态申請記憶體塊時,一定要進行傳回值的判斷。 Linux Libc6采用的機制是在 free 的時候試圖整合相鄰的碎片,使其合并成為一個較大的free空間。    

繼續閱讀