載自:http://blog.csdn.net/masterft/archive/2007/07/27/1711706.aspx 太好所以保留一下,順便與更多的人分享
C++ 內(nèi)存管理詳解
l 內(nèi)存分配方式
在C++中,內(nèi)存分成5個(gè)區(qū),分別是 堆 、 棧 、 自由存儲(chǔ)區(qū) 、 全局 / 靜態(tài)區(qū) 和 常量存儲(chǔ)區(qū) .
棧 :存放函數(shù)參數(shù)以及局部變量,在出作用域時(shí),將自動(dòng)被釋放.棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但分配的內(nèi)存容量有限.
堆 :new分配的內(nèi)存塊(包括數(shù)組,類實(shí)例等),需delete手動(dòng)釋放.如果未釋放,在整個(gè)程序結(jié)束后,OS會(huì)幫你回收掉.
自由存儲(chǔ)區(qū) :malloc分配的內(nèi)存塊,需free手動(dòng)釋放.它和堆有些相似.
全局 / 靜態(tài)區(qū) :全局變量(global)和靜態(tài)變量(static)存于此處.(在以前的C語(yǔ)言中,全局變量又分為初始化的和未初始化的,C++不分)
常量存儲(chǔ)區(qū) :常量(const)存于此處,此存儲(chǔ)區(qū)不可修改.
l 堆與棧的區(qū)別
void f()
{
int *p = new int[5];
}
上面一段代碼就包含了堆與棧.指針P被分配在了棧中,而new出來(lái)的東西則被分配在了堆中,此句可以解釋為”在棧中存放了一個(gè)指向堆內(nèi)存的指針p”.(可否理解為:指針p的值是堆內(nèi)存塊的首地址??????)
主要區(qū)別 :
管理方式不同 : 棧是編譯器自動(dòng)管理的,堆需手動(dòng)釋放
空間大小不同 : 在32位OS下,堆內(nèi)存可達(dá)到4GB的的空間,而棧就小得可憐.(VC6中,棧默認(rèn)大小是1M,當(dāng)然,你可以修改它)
能否產(chǎn)生碎片不同 :對(duì)于棧來(lái)說(shuō),進(jìn)棧/出棧都有著嚴(yán)格的順序(先進(jìn)后出),不會(huì)產(chǎn)生碎片;而堆頻繁的new/delete,會(huì)造成內(nèi)存空間的不連續(xù),容易產(chǎn)生碎片.
生長(zhǎng)方向不同 :棧向下生長(zhǎng),以降序分配內(nèi)存地址;堆向上生長(zhǎng),以升序分配內(nèi)在地址.
分配方式不同 :堆動(dòng)態(tài)分配,無(wú)靜態(tài)分配;棧分為靜態(tài)分配和動(dòng)態(tài)分配,比如局部變量的分配,就是動(dòng)態(tài)分配(alloca函數(shù)),函數(shù)參數(shù)的分配就是動(dòng)態(tài)分配(我想的…).
分配效率不同 :棧是系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持,進(jìn)棧/出棧都有專門的指令,這就決定了棧的效率比較高.堆則不然,它由C/C++函數(shù)庫(kù)提供,機(jī)制復(fù)雜,堆的效率要比棧低得多.
可以看出,棧的效率要比堆高很多,所以,推薦大家盡量用棧.不過,雖然棧有如此多的好處,但遠(yuǎn)沒有堆使用靈活.
l 控制C++的內(nèi)存分配
其實(shí)C++的內(nèi)存管理容易而且安全,因?yàn)楫?dāng)一個(gè)對(duì)象消除時(shí),它的析構(gòu)函數(shù)能夠安全的釋放所有分配的內(nèi)存.在嵌入式系統(tǒng)中,內(nèi)存的分配是一個(gè)常見問題,保守的使用內(nèi)存分配是嵌入式環(huán)境中的第一原則.
當(dāng)你需使用new/delete時(shí),一個(gè)防止堆破碎的通用方法是從不同固定大小的內(nèi)存池中分配不同類型的對(duì)象(??????).對(duì)每個(gè)類重載new和delete就提供了這樣的控制.
class TestClass
{
void *operator new(size_t size);
void operator delete(void *p);
};
void *TestClass::operator new(size_t size)
{
void *p = malloc(size);
return p;
}
void TestClass::operator delete(void *p)
{
free(p);
}
而對(duì)象數(shù)組的分配又不同于單個(gè)對(duì)象的分配,所以你仍需再重載new[]和delete[]操作符.但值得注意的是,對(duì)于C++而言,分配對(duì)象數(shù)組的大小等于數(shù)組參數(shù)的大小再加上額外的對(duì)象數(shù)目的一些字節(jié),所以要盡量避免使用對(duì)象數(shù)組.
class TestClass
{
void *operator new[](size_t size);
void operator delete[](void *p);
};
void *TestClass::operator new[](size_t size)
{
void *p = malloc(size);
return p;
}
void TestClass::operator delete[](void *p)
{
free(p);
}
void main()
{
TestClass *p = new TestClass[10];
delete[] p;
}
l 常見的內(nèi)存錯(cuò)誤及對(duì)策
² 內(nèi)存分配未成功 , 卻使用了它
解決辦法 :在使用之前檢查指針是否為NULL,如果指針p是函數(shù)參數(shù),那么在函數(shù)入口處assert(p!=NULL).如果是用malloc或new申請(qǐng)的話,應(yīng)該用if(p==NULL)進(jìn)行防錯(cuò)處理.
² 內(nèi)存分配成功 , 但未初始化就使用它
解決辦法 : 不要嫌麻煩,記得初始化就行了.
² 內(nèi)存分配成功且已初始化 , 但操作越過了邊界
解決辦法 :此問題通常出現(xiàn)于循環(huán)之中,注意不要多1或少1就行.
² 忘記釋放內(nèi)存
解決辦法 : 含有這個(gè)錯(cuò)誤的函數(shù)每調(diào)用一次就丟失一塊內(nèi)存,造成內(nèi)存耗盡.記得free或delete就行.
² 釋放了內(nèi)存卻繼續(xù)使用它
有三種情況 :
※程序中對(duì)象的關(guān)系過于復(fù)雜,難以搞清哪個(gè)對(duì)象是否已經(jīng)釋放了內(nèi)存.
※函數(shù)中return寫錯(cuò),返回了指向棧中的指針或引用.
※ free或delete后,沒有將指針設(shè)為NULL,產(chǎn)生”野指針”.
l 指針與數(shù)組
C++中,指針和數(shù)組有著不少相似的地方,容易讓我產(chǎn)生錯(cuò)覺,以為它們是等價(jià)的,其實(shí)不然.數(shù)組
在靜態(tài)存儲(chǔ)區(qū)或是棧上被創(chuàng)建,數(shù)組名對(duì)應(yīng)著( 而不是指向 )一塊內(nèi)存,其地址與容量在生命周期內(nèi)保持
不變.而指針可以隨時(shí)指向任意類型的內(nèi)存塊,遠(yuǎn)比數(shù)組靈活,但也危險(xiǎn).
char a[] = "hello";
a[0] = 'x';
char *p = "world";
p[0] = 'y'; //試圖修改常量字符串,編譯器不能發(fā)現(xiàn),執(zhí)行會(huì)報(bào)錯(cuò)
杜絕 ” 野指針 ”
“野指針”不是NULL指針,是指向”垃圾內(nèi)存”的指針.它的缺省值是隨機(jī)的,所以它會(huì)亂指一氣.
產(chǎn)生”野指針”的原因有3種:
1、指針變量沒有被初始化;
2、指針被free/delete后被沒有設(shè)置為NULL;
3、指針操作超越了變量的作用域范圍.如下例,p->fun()時(shí),a已經(jīng)消失.
class A
{
public:
void fun()
{}
};
void Test()
{
A *p;
{
A a;
p = &a; //a的生命周期會(huì)在出作用域時(shí)結(jié)束
}
p->fun(); //p此時(shí)是"野指針"
}
l malloc/free 和 new/delete
有了malloc/free為何還需要new/delete呢? malloc/free是標(biāo)準(zhǔn)庫(kù)函數(shù),而new/delete是運(yùn)算符,它們都可用于申請(qǐng)/釋放動(dòng)態(tài)內(nèi)存.但對(duì)于非基本數(shù)據(jù)類型(比如類對(duì)象)而言, malloc/free無(wú)法自動(dòng)執(zhí)行對(duì)象的構(gòu)造/析構(gòu)函數(shù).而new/delete卻可以.
malloc
函數(shù)malloc的原型:
void *malloc(size_t size);
函數(shù)malloc的使用:
int *p = (int*)malloc(sizeof(int)*length);//length前是乘號(hào)
可見,在使用malloc時(shí)需要進(jìn)行類型轉(zhuǎn)換.而使用sizeof運(yùn)算符也是良好的代碼風(fēng)格.
new
new內(nèi)置了sizeof,所以用起來(lái)寫法更簡(jiǎn)潔.注意,使用new創(chuàng)建對(duì)象數(shù)組時(shí),只能使用對(duì)象的無(wú)參數(shù)構(gòu)造函數(shù).如 Obj *o = new Obj[100];
l 內(nèi)存耗盡怎么辦 ?
解決辦法:
1、判斷指針是否為NULL,如果是立即返回
void fun()
{
A *a = new A();
if(a==NULL)
return;
}
2、判斷指針是否為NULL,如果是立即終止
void fun()
{
A *a = new A();
if(a==NULL)
exit(1);
}
提示:不要不忍心使用exit(1),否則會(huì)害死OS.所以推薦使用方法2.不過搞笑的是,在32位OS上,永遠(yuǎn)也不會(huì)內(nèi)存耗盡.