動態記憶體管理以及malloc,free和new,delete的區別

2020-08-11 19:08:06

爲什麼存在動態記憶體分配?
開闢空間的方式有兩個特點:

  1. 空間開闢大小是固定的。
  2. 陣列在申明的時候,必須指定陣列的長度,它所需要的記憶體在編譯時分配。

但是對於空間的需求,不僅僅是上述的情況。有時候我們需要的空間大小在程式執行的時候才能 纔能知道,那陣列的編譯時開闢空間的方式就不能滿足了。 這時候就只能試試動態存開闢了。

C/C++中的記憶體分佈

  1. 棧又叫堆疊,非靜態區域性變數/函數參數/返回值等等,棧是向下增長的。
  2. 記憶體對映段是高效的I/O對映方式,用於裝載一個共用的動態記憶體庫。使用者可使用系統介面建立共用共
    享記憶體,做進程間通訊。(Linux課程如果沒學到這塊,現在只需要瞭解一下)
  3. 堆用於程式執行時動態記憶體分配,堆是可以上增長的。
  4. 數據段–儲存全域性數據和靜態數據。
  5. 程式碼段–可執行的程式碼/只讀常數。

動態記憶體函數的介紹

malloc和free

void* malloc(size_t size);
  1. 這個函數向記憶體申請一塊連續可用的空間,並返回指向這塊空間的指針。
  2. 如果開闢成功,則返回一個指向開闢好空間的指針。
  3. 如果開闢失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查。
  4. 返回值的型別是 void* ,所以malloc函數並不知道開闢空間的型別,具體在使用的時候使用者自己來決定。
  5. 如果參數 size 爲0,malloc的行爲是標準是未定義的,取決於編譯器。

C語言提供了另外一個函數free,專門是用來做動態記憶體的釋放和回收的,函數原型如下:

void free (void* ptr);
  1. free函數用來釋放動態開闢的記憶體。
  2. 如果參數 ptr 指向的空間不是動態開闢的,那free函數的行爲是未定義的。
  3. 如果參數 ptr 是NULL指針,則函數什麼事都不做。

malloc和free都宣告在 stdlib.h 標頭檔案中。

calloc

void* calloc(size_t num, size_t size);
  1. 函數的功能是爲 num 個大小爲 size 的元素開闢一塊空間,並且把空間的每個位元組初始化爲0。
  2. 與函數 malloc 的區別只在於 calloc 會在返回地址之前把申請的空間的每個位元組初始化爲全0。

realloc

void* realloc(void* ptr, size_t size);
  1. realloc函數的出現讓動態記憶體管理更加靈活。
  2. 有時會我們發現過去申請的空間太小了,有時候我們又會覺得申請的空間過大了,那爲了合理的時候記憶體,
  3. 我們一定會對記憶體的大小做靈活的調整。那 realloc 函數就可以做到對動態開闢記憶體大小的調整。
  4. ptr 是要調整的記憶體地址
  5. size 調整之後新大小
  6. 返回值爲調整之後的記憶體起始位置。
  7. 這個函數調整原記憶體空間大小的基礎上,還會將原來記憶體中的數據移動到 新的空間。

在c++中,有參照了新的動態記憶體開闢函數new,delete

new

void Test()
{
	// 動態申請一個int型別的空間
	int* ptr4 = new int;
 
	// 動態申請一個int型別的空間並初始化爲10
	int* ptr5 = new int(10);
 
	// 動態申請10個int型別的空間
	int* ptr6 = new int[3];
 
	delete ptr4;
	delete ptr5;
	delete[] ptr6; 
}

申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續的空間,使用new[]和delete[]

new和delete是使用者進行動態記憶體申請和釋放的操作符,operator new 和operator delete是系統提供的全域性函數,new在底層呼叫operator new全域性函數來申請空間,delete在底層通過operator delete全域性函數來釋放空間

operator new: 該函數實際通過malloc來申請空間,當malloc申請空間成功時直接返回;申請空間失敗,嘗試執行空 間不足應對措施,如果改應對措施使用者設定了,則繼續申請,否則拋異常。
operator delete: 該函數最終是通過free來釋放空間的

new的原理

  1. 呼叫operator new函數申請空間
  2. 在申請的空間上執行建構函式,完成物件的構造

delete的原理

  1. 在空間上執行析**構函數,完成物件中資源的清理工作

  2. 呼叫operator delete函數釋放物件的空間

new T[N]的原理

  1. 呼叫operator new[]函數,在operator new[]中實際呼叫operator new函數完成N個物件空間的申
  2. 在申請的空間上執行N次建構函式

delete[]的原理

  1. 在釋放的物件空間上執行N次解構函式,完成N個物件中資源的清理
  2. 呼叫operator delete[]釋放空間,實際在operator delete[]中呼叫operator delete來釋放空間

malloc/free和new/delete的區別

malloc/free和new/delete的共同點是:都是從堆上申請空間,並且需要使用者手動釋放。不同的地方是:

  1. malloc和free是函數,new和delete是操作符
  2. malloc申請的空間不會初始化,new可以初始化
  3. malloc申請空間時,需要手動計算空間大小並傳遞,new只需在其後跟上空間的型別即可
  4. malloc的返回值爲void*, 在使用時必須強轉,new不需要,因爲new後跟的是空間的型別
  5. malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常
  6. 申請自定義型別物件時,malloc/free只會開闢空間,不會呼叫建構函式與解構函式,而new在申請空間
    後會呼叫建構函式完成物件的初始化,delete在釋放空間前會呼叫解構函式完成空間中資源的清理
  7. new/delete比malloc和free的效率稍微低點,因爲new/delete的底層封裝了malloc/free

關於記憶體分配的試題

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
 static int staticVar = 1;
 int localVar = 1;
 
 int num1[10] = {1, 2, 3, 4};
 char char2[] = "abcd";
 char* pChar3 = "abcd";
 int* ptr1 = (int*)malloc(sizeof (int)*4);
 int* ptr2 = (int*)calloc(4, sizeof(int));
 int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4);
 free (ptr1);
 free (ptr3);
}
1. 選擇題:
 選項: A.棧 B.堆 C.數據段 D.程式碼段
 globalVar在哪裏?____ staticGlobalVar在哪裏?____
 staticVar在哪裏?____ localVar在哪裏?____
 num1 在哪裏?____
 
 char2在哪裏?____ *char2在哪裏?___
 pChar3在哪裏?____ *pChar3在哪裏?____
 ptr1在哪裏?____ *ptr1在哪裏?____
2. 填空題:
 sizeof(num1) = ____; 

 //C語言中動態記憶體管理方式
 malloc/calloc/realloc和free
 sizeof(char2) = ____; strlen(char2) = ____;
 sizeof(pChar3) = ____; strlen(pChar3) = ____;
 sizeof(ptr1) = ____;