C/C++ 內存管理(malloc/calloc/realloc、free 和 new 、 delete區別;內存泄漏)

C/C++內存分佈

int globalVar = 1;
static int staticGlobalVar = 1;
//globalVar和staticGlobalVar是在main函數之前初始化,在哪都能用,作用域是全局的
/*區別:它倆的鏈接屬性不一樣,globalVar是所有文件可見,staticGlobalVar只在當前文件可見*/
void Test()
{
 static int staticVar = 1; //運行到這裏才初始化,它的作用域在Test函數中,只能在Test函數中使用
 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);
}

選項: A.棧 B.堆 C.數據段 D.代碼段
globalVar在哪裏?數據段
staticGlobalVar在哪裏?數據段
staticVar在哪裏?數據段
localVar在哪裏?
num1 在哪裏?__

char2在哪裏?
*char2在哪裏?_
pChar3在哪裏?__
*pChar3在哪裏?代碼段
ptr1在哪裏? *ptr1在哪裏?_ 堆__

sizeof(num1) = 40;
sizeof(char2) = 5;
strlen(char2) = __4;
sizeof(pChar3) = 4/8;
strlen(pChar3) = 4;
sizeof(ptr1) = 4/8;
在這裏插入圖片描述

  • 棧(堆棧):非靜態局部變量 / 函數參數 / 返回值等等,棧是向下增長的。
  • 內存映射段:是高效的I/O映射方式,用於裝載一個共享的動態內存庫。用戶可使用系統接口創建共享共
    享內存,做進程間通信。
  • 堆:用於程序運行時動態內存分配,堆是可以上增長的。
  • 數據段:存儲全局數據和靜態數據。
  • 代碼段:可執行的代碼 / 只讀常量。

C語言中動態內存管理方式

malloc / calloc / realloc 和 free
malloc:申請空間;
calloc:申請空間並初始化爲0;
realloc:對原來已經有的空間進行擴容。

C++內存管理方式

C語言內存管理方式在C++中可以繼續使用,但有些地方就無能爲力而且使用起來比較麻煩,因此C++又提出
了自己的內存管理方式:通過new和delete操作符進行動態內存管理。

new / delete操作內置類型

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

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

在這裏插入圖片描述

new和delete操作自定義類型

在申請自定義類型的空間時,new 會調用構造函數,delete會調用析構函數,而malloc 和 free不會。

class Test
{
public:
 Test()
 : _data(0)
 {
 cout<<"Test():"<<this<<endl;
 }
 ~Test()
 {
 cout<<"~Test():"<<this<<endl;
 }
 
private:
 int _data;
};
void Test2()
{
 // 申請單個Test類型的空間
 Test* p1 = (Test*)malloc(sizeof(Test));
 free(p1);
 
 // 申請10個Test類型的空間
 Test* p2 = (Test*)malloc(sizoef(Test) * 10);
 free(p2);
}

void Test2()
{
 // 申請單個Test類型的對象
 Test* p1 = new Test;
 delete p1;
 
 // 申請10個Test類型的對象
 Test* p2 = new Test[10];
 delete[] p2; 
 }

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在釋放空間前會調用析構函數完成空間中資源的清理。

new和delete的實現原理

如果申請的是內置類型的空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請和
釋放的是單個元素的空間,new[]和delete[]申請的是連續空間,而且new在申請空間失敗時會拋異常,
malloc會返回NULL。

自定義類型

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來釋放空間

內存泄露

內存泄漏指因爲疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。

內存泄露的危害:長期運行的程序出現內存泄漏,影響很大,如操作系統、後臺服務等等,出現內存泄漏會導致響應越來越慢,最終卡死。

void MemoryLeaks()
 {
 // 1.內存申請了忘記釋放
 int* p1 = (int*)malloc(sizeof(int));
 int* p2 = new int;
 
 // 2.異常安全問題
 int* p3 = new int[10];
 
 Func(); // 這裏Func函數拋異常導致 delete[] p3未執行,p3沒被釋放.
 
 delete[] p3;
 }

內存泄露的分類

  • 堆內存泄露:程序執行中依據須要分配通過malloc / calloc / realloc / new等從堆中分配的一塊內存,
    用完後必須通過調用相應的 free或者delete 刪掉。假設程序的設計錯誤導致這部分內存沒有被釋放,那
    麼以後這部分空間將無法再被使用,就會產生內存泄露。
  • 系統資源泄露:程序使用系統分配的資源,比方套接字、文件描述符、管道等沒有使用對應的函數釋放掉,導致系統資源的浪費,嚴重可導致系統效能減少,系統執行不穩定。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章