C/C++內(nèi)存管理
C/C++中程序內(nèi)存區(qū)域劃分
C/C++中程序內(nèi)存區(qū)域大致劃分六個(gè)部分:內(nèi)核空間(用戶代碼不能讀寫)、棧(向下增長(zhǎng))、內(nèi)存映射段(文件映射、動(dòng)態(tài)庫(kù)、匿名映射)、堆(向上增長(zhǎng))、數(shù)據(jù)段(全局?jǐn)?shù)據(jù)、靜態(tài)數(shù)據(jù))、代碼段(可執(zhí)行代碼、只讀常量)。
各自內(nèi)存區(qū)域功能
- 棧,又叫做堆棧。非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等;棧是向下增長(zhǎng)的。
- 內(nèi)存映射段,高效的I/O映射方式,用于裝載一個(gè)共享的動(dòng)態(tài)內(nèi)存庫(kù)。用戶可使用系統(tǒng)接口創(chuàng)建共享內(nèi)存,做進(jìn)程間通信。
- 堆,用于程序運(yùn)行時(shí)動(dòng)態(tài)內(nèi)存分配,堆是向上增長(zhǎng)的。
- 數(shù)據(jù)段,存儲(chǔ)全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)。
- 代碼段,可執(zhí)行的代碼、只讀常量。
相關(guān)例題
針對(duì)下面代碼,完成下面三道小題:
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);
}
//1. 選擇題:
//選項(xiàng) : A.棧 ?B.堆 ?C.數(shù)據(jù)段(靜態(tài)區(qū)) ?D.代碼段(常量區(qū))
//globalVar在哪里?____ ? staticGlobalVar在哪里?____
//staticVar在哪里?____ ? localVar在哪里?____
//num1 在哪里?____
//
//char2在哪里?____ * char2在哪里?___
//pChar3在哪里?____ * pChar3在哪里?____
//ptr1在哪里?____ * ptr1在哪里?____
//2. 填空題:
//sizeof(num1) = ____;
//sizeof(char2) = ____; ? ? ?strlen(char2) = ____;
//sizeof(pChar3) = ____; ? ? strlen(pChar3) = ____;
//sizeof(ptr1) = ____;
//3. sizeof 和 strlen 區(qū)別?
①對(duì)于內(nèi)存區(qū)域理解
選擇題組一
A.棧 ?B.堆 ?C.數(shù)據(jù)段(靜態(tài)區(qū)) ?D.代碼段(常量區(qū))
- globalVar在哪里?
- staticGlobalVar在哪里?
- staticVar在哪里?
- localVar在哪里?
- num1 在哪里?
解析
int globalVar = 1;
globalVar是全局變量;“數(shù)據(jù)段,存儲(chǔ)全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)”,故選C.數(shù)據(jù)段(靜態(tài)區(qū));
static int staticGlobalVar = 1;
staticGlobalVar是全局靜態(tài)變量,“數(shù)據(jù)段,存儲(chǔ)全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)?!惫蔬xC.數(shù)據(jù)段(靜態(tài)區(qū));
void Test() { static int staticVar = 1; }
staticVar是局部靜態(tài)變量,“數(shù)據(jù)段,存儲(chǔ)全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)?!惫蔬xC.數(shù)據(jù)段(靜態(tài)區(qū));
void Test() { int localVar = 1; }
localVar是局部靜態(tài)變量,“棧,又叫做堆棧。非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等;棧是向下增長(zhǎng)的?!惫蔬xA.棧;
void Test() { int num1[10] = { 1, 2, 3, 4 }; }
num1是局部非靜態(tài)變量,“棧,又叫做堆棧。非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等;棧是向下增長(zhǎng)的?!惫蔬xA.棧;
選擇題組二
A.棧 ?B.堆 ?C.數(shù)據(jù)段(靜態(tài)區(qū)) ?D.代碼段(常量區(qū))
- char2在哪里?
- *char2在哪里?
- pChar3在哪里?
- *pChar3在哪里?
- ptr1在哪里?
- *ptr1在哪里??
解析
void Test() { char char2[] = "abcd"; }
char2是局部非靜態(tài)變量,“棧,又叫做堆棧。非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等;棧是向下增長(zhǎng)的?!惫蔬xA.棧;
void Test() { char char2[] = "abcd"; }
數(shù)組是首元素的地址,所以對(duì)其解引用仍在棧。
void Test() { const char* pChar3 = "abcd"; }
pchar3是地址,是局部非靜態(tài)變量,注:const修飾的是字符串“abcd”為常量?!?span style="color:#be191c;">棧,又叫做堆棧。非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等;棧是向下增長(zhǎng)的?!惫蔬xA.棧;
void Test() { const char* pChar3 = "abcd"; }
pchar3是地址,指向一塊存儲(chǔ)字符串“abcd”的空間,*pchar3是對(duì)地址解引用,為字符串“abcd”。const修飾的是字符串“abcd”,所以為常量,“代碼段,可執(zhí)行的代碼、只讀常量?!惫蔬xD;
void Test() { int* ptr1 = (int*)malloc(sizeof(int) * 4); }
ptr1是地址,是局部非靜態(tài)變量,“棧,又叫做堆棧。非靜態(tài)局部變量、函數(shù)參數(shù)、返回值等;棧是向下增長(zhǎng)的?!惫蔬xA.棧;
void Test() { int* ptr1 = (int*)malloc(sizeof(int) * 4); }
pchar3是地址,指向一塊動(dòng)態(tài)開(kāi)辟的空間,“堆,用于程序運(yùn)行時(shí)動(dòng)態(tài)內(nèi)存分配,堆是向上增長(zhǎng)的?!惫蔬xB。
②對(duì)于sizeof以及strlen理解
填空題
- sizeof(num1) = ____;
- sizeof(char2) = ____;
- strlen(char2) = ____;
- sizeof(pChar3) = ____;
- strlen(pChar3) = ____;
- sizeof(ptr1) = ____;
解析
sizeof(num1):num1是開(kāi)辟10個(gè)空間的數(shù)組,int為4字節(jié);;int globalVar = 1;
sizeof(char2) :“abcd”字符串中隱藏了'\0',有5個(gè)字符;void Test() { char char2[] = "abcd"; }
strlen(char2) :strlen計(jì)算'\0'前面有幾個(gè)字符,4個(gè)字符;void Test() { char char2[] = "abcd"; }
sizeof(pChar3):pchar3是地址,是地址就是4或者8;void Test() { const char* pChar3 = "abcd"; }
strlen(pChar3) :strlen計(jì)算'\0'前面有幾個(gè)字符,4個(gè)字符;void Test() { const char* pChar3 = "abcd"; }
sizeof(ptr1):ptr1是地址,是地址就是4或者8;void Test() { int* ptr1 = (int*)malloc(sizeof(int) * 4); }
③sizeof和strlen區(qū)別
【點(diǎn)擊查看】sizeof和strlen的區(qū)別、sizeof(a)與sizeof(a+0)區(qū)別在哪里?sizeof(*a)、sizeof(a+1)、sizeof(a[1])...... | 筆試題分析| 超全
C++中動(dòng)態(tài)內(nèi)存管理
C語(yǔ)言內(nèi)存管理方式在C++中可以繼續(xù)使用,但是有些地方就無(wú)能為力,而且使用起來(lái)比較麻煩,因此C++又提出了自己的內(nèi)存管理方式:通過(guò)new和delete操作符進(jìn)行動(dòng)態(tài)內(nèi)存管理。
new/delete操作內(nèi)置類型
new/delete操作內(nèi)置類型使用語(yǔ)法
- 申請(qǐng)和釋放單個(gè)元素的空間,使用new和delete操作符,申請(qǐng)和釋放連續(xù)的空間,使用new[]和delete[],注意要匹配起來(lái)使用。?
數(shù)據(jù)類型 變量名 = new 數(shù)據(jù)類型;
數(shù)據(jù)類型 變量名 = new 數(shù)據(jù)類型(初始化值);
數(shù)據(jù)類型 變量名 = new 數(shù)據(jù)類型[個(gè)數(shù)]);
delete 變量名;
delete []變量名;
new/delete操作內(nèi)置類型使用示范
void Test()
{
//動(dòng)態(tài)申請(qǐng)一個(gè)int類型的空間
int* ptr4 = new int;
//動(dòng)態(tài)申請(qǐng)一個(gè)int類型的空間并初始化為10
int* ptr5 = new int(10);
//動(dòng)態(tài)申請(qǐng)三個(gè)int類型的空間
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
new/delete操作自定義類型
new/delete操作自定義類型語(yǔ)法
類名* 地址名 = new 類名[];
new/delete操作自定義類型使用示范
class A
{
private:
int _a;
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a = 0)" << this << endl;
}
~A()
{
cout << "~A()" << this << endl;
}
};
int main()
{
A* p2 = new A[10];
delete[] p2;
return 0;
}
new/delete和malloc/free區(qū)別
- 對(duì)于內(nèi)置類型,幾乎都一樣;
- 對(duì)于自定義類型,new/delete除了開(kāi)辟空間還會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)。
operator new與operator delete函數(shù)
- new和delete是用戶進(jìn)行動(dòng)態(tài)內(nèi)存申請(qǐng)和釋放的操作符;
- operator new和operator delete是系統(tǒng)提供的全局函數(shù);
- new在底層調(diào)用operator new全局函數(shù)來(lái)申請(qǐng)空間;
- delete在底層通過(guò)operator delete全局函數(shù)來(lái)釋放空間。
- operator new實(shí)際也是通過(guò)malloc來(lái)申請(qǐng)空間的;
- 如果malloc申請(qǐng)空間成功就直接返回,否則執(zhí)行用戶提供的空間是不足應(yīng)對(duì)措施的;‘’如果用戶提供該措施就繼續(xù)申請(qǐng),否則就拋異常;
- operator delete最終是通過(guò)free來(lái)釋放空間的。
new和delete的實(shí)現(xiàn)原理
內(nèi)置類型
如果申請(qǐng)的是內(nèi)置類型空間,new和malloc,delete和free基本類似,不同的地方是:
- new/delete申請(qǐng)和釋放的是單個(gè)元素的空間;
- naw[]和delete[]申請(qǐng)的是連續(xù)空間;
- 而且new在申請(qǐng)空間時(shí)會(huì)拋出異常,malloc會(huì)返回NULL。
自定義類型
new的原理
- 調(diào)用operator new函數(shù)申請(qǐng)空間;
- 在申請(qǐng)的空間上執(zhí)行構(gòu)造函數(shù),完成對(duì)象的構(gòu)造。
delete的原理
- 在空間上執(zhí)行析構(gòu)函數(shù),完成對(duì)象中資源的清理工作;
- 調(diào)用operator delete函數(shù)釋放對(duì)象的空間。
new T[N] 的原理
- 調(diào)用operator new[] 函數(shù),在operator new[] 中實(shí)際調(diào)用operator new函數(shù)完成N個(gè)對(duì)象空間的申請(qǐng);
- 在申請(qǐng)的空間上執(zhí)行N次構(gòu)造函數(shù)。
delete[] 的原理
- 在釋放的對(duì)象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個(gè)對(duì)象中資源的清理;
- 調(diào)用operator delete[] 釋放空間,實(shí)際在operator delete[] 中調(diào)用operator delete來(lái)釋放空間。
定位new表達(dá)式(placement-new)
定位new表達(dá)式(placement-new)語(yǔ)法
- 定位new表達(dá)式是在已經(jīng)分配的原始內(nèi)存空間中調(diào)用了構(gòu)造函數(shù)初始化一個(gè)對(duì)象。
- 一般是配合內(nèi)存池使用
- (因?yàn)閮?nèi)存池分配出的內(nèi)存沒(méi)有初始化,所以如果是自定義類型的對(duì)象,需要使用new定義表達(dá)式進(jìn)行顯示調(diào)用構(gòu)造函數(shù)進(jìn)行初始化)。
new(指針名)數(shù)據(jù)類型;
new(指針名)數(shù)據(jù)類型(初始化列表);
定位new表達(dá)式(placement-new)使用實(shí)例
class A
{
private:
int _a;
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a = 0)" << this << endl;
}
~A()
{
cout << "~A()" << this << endl;
}
};
//定位new、replacement new
int main()
{
A* p1 = (A*)operator new(sizeof(A));
new(p1)A(10);
p1->~A();
operator delete(p1);
return 0;
}
常見(jiàn)面試題
malloc/free和new/delete的區(qū)別
malloc/free和new/delete的共同點(diǎn)是:都是從堆上申請(qǐng)空間,并且都需要用戶手動(dòng)釋放。
malloc/free和new/delete不同點(diǎn):
- malloc和free是函數(shù),new和delete是操作符;
- malloc申請(qǐng)的空間不會(huì)初始化,new可以初始化;
- malloc申請(qǐng)空間時(shí),需要手動(dòng)計(jì)算空間大小并傳遞,new只需要在其后跟上空間的類型即可,如果是多個(gè)對(duì)象,[] 中指定對(duì)象個(gè)數(shù)即可;
- malloc返回值為void*,在使用時(shí)必須強(qiáng)轉(zhuǎn),new不需要,因?yàn)閚ew后跟的是空間的類型;
- malloc申請(qǐng)空間失敗時(shí),返回的是NULL,因此使用時(shí)必須判空,new不需要,但是new需要捕獲異常;
- 申請(qǐng)自定義類型對(duì)象時(shí),malloc和free只會(huì)開(kāi)辟空間,不會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),而new在申請(qǐng)空間后會(huì)調(diào)用構(gòu)造函數(shù)完成對(duì)象的初始化,delete在釋放空間前會(huì)調(diào)用析構(gòu)函數(shù)完成空間中資源的清理。
內(nèi)存泄漏
什么是內(nèi)存泄漏,內(nèi)存泄漏的危害
內(nèi)存泄漏
內(nèi)存泄漏,是指因?yàn)槭韬龌蛘咤e(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。
內(nèi)存泄漏并不是指在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因?yàn)樵O(shè)計(jì)錯(cuò)誤,失去了對(duì)該段內(nèi)存的控制,因而造成了內(nèi)存的浪費(fèi)。
內(nèi)存泄漏的危害
長(zhǎng)期運(yùn)行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺(tái)服務(wù)等等,出現(xiàn)內(nèi)存泄漏會(huì)導(dǎo)致響應(yīng)越來(lái)越慢,最終卡死。
void MemoryLeaks()
{
? // 1.內(nèi)存申請(qǐng)了忘記釋放
?int* p1 = (int*)malloc(sizeof(int));
?int* p2 = new int;
?
?// 2.異常安全問(wèn)題
?int* p3 = new int[10];
?
?Func(); // 這里Func函數(shù)拋異常導(dǎo)致 delete[] p3未執(zhí)行,p3沒(méi)被釋放.
?
?delete[] p3;
}
內(nèi)存泄漏分類
堆內(nèi)存泄漏(Heap leak)
堆內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過(guò)malloc / calloc / realloc / new等從堆中分配的一塊內(nèi)存,用完后必須通過(guò)調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計(jì)錯(cuò)誤導(dǎo)致這部分內(nèi)存沒(méi)有被釋放,那么以后這部分空間將無(wú)法再被使用,就會(huì)產(chǎn)生Heap Leak。
系統(tǒng)資源泄漏
指程序使用系統(tǒng)分配的資源,比方套接字、文件描述符、管道等沒(méi)有使用對(duì)應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費(fèi),嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-833292.html
如何避免內(nèi)存泄漏
- 工程前期良好的設(shè)計(jì)規(guī)范,養(yǎng)成良好的編碼規(guī)范,申請(qǐng)的內(nèi)存空間記著匹配的去釋放。ps:這個(gè)理想狀態(tài)。但是如果碰上異常時(shí),就算注意釋放了,還是可能會(huì)出問(wèn)題。需要下一條智能指針來(lái)管理才有保證。
- 采用RAII思想或者智能指針來(lái)管理資源。
- 有些公司內(nèi)部規(guī)范使用內(nèi)部實(shí)現(xiàn)的私有內(nèi)存管理庫(kù)。這套庫(kù)自帶內(nèi)存泄漏檢測(cè)的功能選項(xiàng)。
- 出問(wèn)題了使用內(nèi)存泄漏工具檢測(cè)。ps:不過(guò)很多工具都不夠靠譜,或者收費(fèi)昂貴。
如何避免內(nèi)存泄漏:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-833292.html
- 事前預(yù)防。智能指針等。
- 事后查錯(cuò)型。泄漏檢測(cè)工具。
到了這里,關(guān)于C++ || C/C++內(nèi)存管理 | C++動(dòng)態(tài)內(nèi)存管理方式 | operator new/delete函數(shù) | new和delete實(shí)現(xiàn)原理 | 定位new表達(dá)式 | 內(nèi)存泄漏的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!