?
目錄
C/C++內存分布
?C語言中動態內存管理方式:malloc/calloc/realloc/free
C++內存管理方式
new/delete操作內置類型
new和delete操作自定義類型
operator new與operator delete函數
?new和delete的實現原理
內置類型
自定義類型
內存泄漏
概念
內存泄漏分類
?C/C++內存分布
先來看看下面一段代碼和相關問題
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";const 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);
}
?這些問題就涉及到C/C++程序的內存分布問題
- 棧區(stack):在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。棧區主要存放運行函數而分配的局部變量、函數參數、返回數據、返回地址等。
- 堆區(heap):?般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS(操作系統)回收。分配方式類似于鏈表。
- 數據段(靜態區)(static):存放全局變量、靜態數據。程序結束后由系統釋放。
- 代碼段:存放函數體(類成員函數和全局函數)的二進制代碼(可執行的代碼/只讀常量
)。 - 內存映射段 是高效的I/O映射方式,用于裝載一個共享的動態內存庫。用戶可使用系統接口
創建共享共享內存,做進程間通信。
?
??C語言中動態內存管理方式:malloc/calloc/realloc/free
void Test()
{int* p1 = (int*)malloc(sizeof(int));free(p1);int* p2 = (int*)calloc(4, sizeof(int));int* p3 = (int*)realloc(p2, sizeof(int) * 10);free(p3);
}
- malloc:這個函數向內存申請?塊連續可用的空間,并返回指向這塊空間的指針。參數 size 指的是申請的空間的大小
- calloc:函數的功能是為 num 個大小為 size 的元素開辟?塊空間,并且把空間的每個字節初始化為0。
- realloc:realloc 函數可以對動態開辟內存大小進行調整,返回值為調整之后的內存起始位置。
- free:free函數用來釋放動態開辟的內存。
?C++內存管理方式
C語言內存管理方式在C++中可以繼續使用,但有些地方就無能為力,而且使用起來比較麻煩,因此C++又提出了自己的內存管理方式:通過new和delete操作符進行動態內存管理。
?new/delete操作內置類型
void Test()
{// 動態申請一個int類型的空間int* ptr4 = new int;// 動態申請一個int類型的空間并初始化為10int* ptr5 = new int(10);// 動態申請10個int類型的空間int* ptr6 = new int[3];delete ptr4;delete ptr5;delete[] ptr6;
}
?new返回的是該數據類型的指針
注意:申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續的空間,使用
new[]和delete[],注意:匹配起來使用。
?new和delete操作自定義類型
new/delete 和 malloc/free最大區別是 new/delete對于【自定義類型】除了開空間還會調用構造函數和析構函數
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
int main()
{// new/delete 和 malloc/free最大區別是 new/delete對于【自定義類型】除了開空間還會調用構造函數和析構函數A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);free(p1);delete p2;// 內置類型是幾乎是一樣的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int;free(p3);delete p4;A* p5 = (A*)malloc(sizeof(A) * 10);A* p6 = new A[10];free(p5);delete[] p6;return 0;
}
?new和malloc時的對比:
delete和free的對比:
注意:在申請自定義類型的空間時,new會調用構造函數,delete會調用析構函數,而malloc與
free不會。
我們在使用malloc時,常常需要進行如下的類型檢查,防止內存開辟失敗:
struct Node
{int val;Node* next;
};
//以創建一個鏈表的節點為例
Node* CreateNode(int val)
{Node* newnode = (Node*)malloc(sizeof(Node));if (newnode = NULL){perror("malloc!");exit(-1);}newnode->val = val;newnode->next = NULL;return newnode;
}
?而在C++中,我們使用new進行開辟空間時,不需要進行這樣的手動檢查,new在開辟失敗時,會拋異常。
?operator new與operator delete函數
new和delete是用戶進行動態內存申請和釋放的操作符,operator new 和operator delete是
系統提供的全局函數,new在底層調用operator new全局函數來申請空間,delete在底層通過
operator delete全局函數來釋放空間。
operator new:該函數實際通過malloc來申請空間,當malloc申請空間成功時直接返回;申請空間
失敗,嘗試執行空間不足應對措施,如果改應對措施用戶設置了,則繼續申請,否則拋異常。
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申請內存失敗了,這里會拋出bad_alloc 類型異常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
operator delete: 該函數最終是通過free來釋放空間的
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}/*
free的實現
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
通過上述兩個全局函數的實現知道,operator new 實際也是通過malloc來申請空間,如果
malloc申請空間成功就直接返回,否則執行用戶提供的空間不足應對措施,如果用戶提供該措施
就繼續申請,否則就拋異常。operator delete 最終是通過free來釋放空間的。
?
??new和delete的實現原理
?內置類型
如果申請的是內置類型的空間,new和malloc,delete和free基本類似,不同的地方是:
new/delete申請和釋放的是單個元素的空間,new[] 和 delete[] 申請的是連續空間,而且new在申請空間失敗時會拋異常,malloc會返回NULL。
?自定義類型
new的原理
- 1. 調用operator new函數申請空間
- 2. 在申請的空間上執行構造函數,完成對象的構造
delete的原理
- 在空間上執行析構函數,完成對象中資源的清理工作
- 調用operator delete函數釋放對象的空間
new T[N]的原理
- 調用operator new[]函數,在operator new[]中實際調用operator new函數完成N個對象空間的申請
- 在申請的空間上執行N次構造函數
delete[]的原理
- 在釋放的對象空間上執行N次析構函數,完成N個對象中資源的清理
- 調用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;
}
?內存泄漏分類
C/C++程序中一般我們關心兩種方面的內存泄漏:
堆內存泄漏(Heap leak)
- 堆內存指的是程序執行中依據須要分配通過malloc / calloc / realloc / new等從堆中分配的一塊內存,用完后必須通過調用相應的 free或者delete 刪掉。假設程序的設計錯誤導致這部分內存沒有被釋放,那么以后這部分空間將無法再被使用,就會產生Heap Leak。
系統資源泄漏
- 指程序使用系統分配的資源,比方套接字、文件描述符、管道等沒有使用對應的函數釋放掉,導致系統資源的浪費,嚴重可導致系統效能減少,系統執行不穩定
____________________
?感謝你的閱讀,希望本文能夠對你有所幫助。如果你喜歡我的內容,記得點贊關注收藏我的博客,我會繼續分享更多的內容。?