1. 程式人生 > >常見動態記憶體分配malloc()/free()、new/delete使用方法及常見錯誤

常見動態記憶體分配malloc()/free()、new/delete使用方法及常見錯誤

1.動態記憶體分配的幾種方式
①從靜態儲存區分配記憶體,記憶體在編譯時已經分配好了,這塊記憶體在整個程式執行期間都存在,比如全域性變數
②從棧上分配記憶體,函式體結束時,棧記憶體自動銷燬,比如區域性變數
③從堆上開闢記憶體,比如malloc()/new
2.malloc()和free()的使用方法
malloc()/free()用來動態分配記憶體,使用方法如下:

void *malloc(size_t  size);//動態一塊開闢size位元組大小的記憶體
void free(void* ptr);               //釋放ptr指向的一塊記憶體空間

舉例說明malloc()/free()的使用方法

int *p=(int*) malloc(100);    //指標p指向一個記憶體空間為100個位元組的地址
int *p=(int*) malloc(25*sizeof(int));    //指標p指向25個整型的空間

由於malloc()的返回值型別為void*,所以在函式前面要進行相應的強制型別轉換
另外還有兩個記憶體分配的函式calloc()和realloc(),它們的函式原型如下:

void* calloc(size_t num,size_t size);//在記憶體的動態儲存區分配N塊長度為size位元組的記憶體,並初始化為0
void* realloc (void* ptr, size_t size);     //將ptr的記憶體大小增大到size,增加的記憶體沒有進行初始化

calloc()和realloc()的使用舉例如下:

int  *p = (int*) calloc (i,sizeof(int));
int *p = (int*) realloc (numbers, count * sizeof(int));

malloc()和calloc()的主要區別主要有:calloc在返回指向記憶體的指標之前會首先將記憶體初始化為0;calloc()的引數包含所需元素的數量和每 個元素所包含的位元組數
realloc()用於修改原先已經分配記憶體塊的大小,可以擴大和縮小事先分配好的記憶體塊大小。如果要擴大記憶體,原來的記憶體大小保持不變,直接在原來的記憶體塊尾部增加新的記憶體塊,且不進行初始化;如果原先的記憶體塊無法擴充,則新開闢一塊記憶體,並複製原記憶體塊中的內容,原先記憶體塊失效再無法進行訪問;如果要縮小記憶體,則直接在原來記憶體塊的尾部直接刪減即可。
3.new和delete的使用方法


new/delete 動態管理物件
new[]/delete[] 動態管理物件陣列
下面舉例說明new/delete的使用方法:

int *p1=new int;   //動態分配4個字(一個整型)節的空間單個數據
int *p2=new int(3); //動態分配4個字(一個整型)節的空間並初始化為3
int  *p3=new  int[3];    //呼叫operator new動態分配12個位元組(3個int)的空間,呼叫了3次建構函式,operator new相當                                         於malloc

delete p1;
delete p2;
delete[] p2;                //呼叫3次解構函式

malloc()/free(),new/delete,new[]/delete[]一定要記得匹配使用哦!否則會記憶體洩漏甚至崩潰
new/delete,new[]/delete[]的底層呼叫
new/delete:
這裡寫圖片描述
new[]/delete[]
這裡寫圖片描述
4.malloc()/free()和new/delete的區別
有了malloc()/free(),為什麼還要用new/delete,二者有何區別?
malloc()/free()和new/delete都可以用於動態記憶體的申請和釋放。但對於非靜態內部資料型別的物件而言,物件在消亡之前要自動的執行解構函式,由於malloc/free是庫函式,而不是運算子,不在編譯器控制範圍之內不能把執行建構函式和解構函式的任務強加給malloc()/free(),所以只能用new/delete運算子。
區別:①new是構造一個物件,並呼叫建構函式來初始化物件,其實在new的所有操作過程中總共分為兩步: 申請內 存;呼叫建構函式。在呼叫delete時,先呼叫解構函式,再銷燬堆記憶體
②malloc()/free() C/C++標準庫函式,new/delete是C++的操作符
③new/delete支援過載,過載之後就成了函式。
④new/delete內部實現實質上也呼叫了malloc()/free()
⑤malloc()/free()需要手動計算型別大小,返回void*指標,new/delete可以自己計算型別大小,返回對應型別的指標
⑥malloc()申請記憶體失敗會返回一個空指標,new申請記憶體失敗會拋bad_alloc異常或者返回空指標
5.什麼情況下會malloc()和new失敗?
①記憶體不足時,會出現記憶體失敗
②前面的程式出現了一些記憶體的越界訪問,導致malloc()分配函式所涉及的一些資訊被破壞,下次使用malloc()申請記憶體時就會malloc() 失敗,返回NULL
6.動態記憶體傳遞問題
在函式體內定義的臨時變數函式結束時,棧上的記憶體就會自動銷燬,如果想返回變數,可以在堆上開闢一段新的記憶體

void GetMemory(char *p,int mum)
{
   p=(char*)malloc(sizeof(char)*num);
}
int main()
{
   char *str=NULL;
   GetMemory(str,10);
   strcpy(str,"hello");
}

這段程式存在兩個嚴重的問題:
①p實質上只是str的一份臨時備份,雖然p申請了堆記憶體,但返回main()函式時,str還是NULL,執行strcpy()函式時,會導致程式崩潰
②從Memory()函式返回時不能獲得堆中的地址,那塊記憶體就不能被繼續使用,也得不到釋放,因此呼叫一次Memory()函式就會產生mun個位元組的記憶體洩漏
解決以上的記憶體洩漏問題有如下三種方法:
①C語言中,採用指向指標的指標解決這個問題,把str的地址傳遞給函式GetMemory()
②在C++中,傳遞str指標的引用
③使用返回值來傳遞記憶體

void GetMemary1(char **p,int num)
{
  *p=(char*)malloc(sizeof(char)*num);
}
void GetMemary2(char * &p,int num)
{
  p=(char*)malloc(sizeof(char)*num);
}
void *GetMemary3(int num)
{
  char *p=(char*)malloc(sizeof(char)*num);
return p;
}