C++-內存管理

 
 
C++ 內存管理詳解
 
 
1,內存分配方式
在C++中,內存分紅5個區,分別是 自由存儲區全局/ 靜態區常量存儲區.
:存放函數參數以及局部變量,在出做用域時,將自動被釋放.棧內存分配運算內置於處理器的指令集中,效率很高,但分配的內存容量有限.
:new分配的內存塊(包括數組,類實例等),需delete手動釋放.若是未釋放,在整個程序結束後,OS會幫你回收掉.
自由存儲區:malloc分配的內存塊,需free手動釋放.它和堆有些類似.
全局 / 靜態區:全局變量(global)和靜態變量(static)存於此處.(在之前的C語言中,全局變量又分爲初始化的和未初始化的,C++不分)
常量存儲區:常量(const)存於此處,此存儲區不可修改.
 
 
2,堆與棧的區別
void f()
{
       int *p = new int[5];
}
上面一段代碼就包含了堆與棧.指針P被分配在了棧中,而new出來的東西則被分配在了堆中,此句能夠解釋爲」在棧中存放了一個指向堆內存的指針p」.(能否理解爲:指針p的值是堆內存塊的首地址??????)
主要區別:
管理方式不一樣: 棧是編譯器自動管理的,堆需手動釋放
空間大小不一樣: 在32位OS下,堆內存可達到4GB的的空間,而棧就小得可憐.(VC6中,棧默認大小是1M,固然,你能夠修改它)
可否產生碎片不一樣:對於棧來講,進棧/出棧都有着嚴格的順序(先進後出),不會產生碎片;而堆頻繁的new/delete,會形成內存空間的不連續,容易產生碎片.
生長方向不一樣:棧向下生長,以降序分配內存地址;堆向上生長,以升序分配內在地址.
分配方式不一樣:堆動態分配,無靜態分配;棧分爲靜態分配和動態分配,好比局部變量的分配,就是動態分配(alloca函數),函數參數的分配就是動態分配(我想的…).
分配效率不一樣:棧是系統提供的數據結構,計算機會在底層對棧提供支持,進棧/出棧都有專門的指令,這就決定了棧的效率比較高.堆則否則,它由C/C++函數庫提供,機制複雜,堆的效率要比棧低得多.
能夠看出,棧的效率要比堆高不少,因此,推薦你們儘可能用棧.不過,雖然棧有如此多的好處,但遠沒有堆使用靈活.
 
 
3,控制C++的內存分配
其實C++的內存管理容易並且安全,由於當一個對象消除時,它的析構函數可以安全的釋放全部分配的內存.在嵌入式系統中,內存的分配是一個常見問題,保守的使用內存分配是嵌入式環境中的第一原則.
當你需使用new/delete時,一個防止堆破碎的通用方法是從不一樣固定大小的內存池中分配不一樣類型的對象(??????).對每一個類重載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);
}
而對象數組的分配又不一樣於單個對象的分配,因此你仍需再重載new[]和delete[]操做符.但值得注意的是,對於C++而言,分配對象數組的大小等於數組參數的大小再加上額外的對象數目的一些字節,因此要儘可能避免使用對象數組.
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;
}
 
 
4, 常見的內存錯誤及對策
²         內存分配未成功 , 卻使用了它
解決辦法:在使用以前檢查指針是否爲NULL,若是指針p是函數參數,那麼在函數入口處assert(p!=NULL).若是是用malloc或new申請的話,應該用if(p==NULL)進行防錯處理.
²         內存分配成功 , 但未初始化就使用它
    解決辦法 :不要嫌麻煩,記得初始化就好了.
²         內存分配成功且已初始化 , 但操做越過了邊界
    解決辦法:此問題一般出現於循環之中,注意不要多1或少1就行.
²         忘記釋放內存
   
解決辦法 :含有這個錯誤的函數每調用一次就丟失一塊內存,形成內存耗盡.記得free或delete就行.
²         釋放了內存卻繼續使用它
有三種狀況 :
※程序中對象的關係過於複雜,難以搞清哪一個對象是否已經釋放了內存.
※函數中return寫錯,返回了指向棧中的指針或引用.
※ free或delete後,沒有將指針設爲NULL,產生」野指針」.
 
 
5,指針與數組
C++中,指針和數組有着很多類似的地方,容易讓我產生錯覺,覺得它們是等價的,其實否則.數組
       在靜態存儲區或是棧上被建立,數組名對應着( 而不是指向)一塊內存,其地址與容量在生命週期內保持
       不變.而指針能夠隨時指向任意類型的內存塊,遠比數組靈活,但也危險.
       char a[] = "hello";
a[0] = 'x';
char *p = "world";
p[0] = 'y'; //試圖修改常量字符串,編譯器不能發現,執行會報錯
杜絕 野指針
「野指針」不是NULL指針,是指向」垃圾內存」的指針.它的缺省值是隨機的,因此它會亂指一氣.
產生」野指針」的緣由有3種:
一、指針變量沒有被初始化;
二、指針被free/delete後被沒有設置爲NULL;
三、指針操做超越了變量的做用域範圍.以下例,p->fun()時,a已經消失.
class A
{
public:
       void fun()
       {}
};
void Test()
{
       A *p;
       {
              A a;
              p = &a; //a的生命週期會在出做用域時結束
       }
       p->fun(); //p此時是"野指針"
}
 
 
6,malloc/free 和new/delete
有了malloc/free爲什麼還須要new/delete呢? malloc/free是標準庫函數,而new/delete是運算符,它們均可用於申請/釋放動態內存.但對於非基本數據類型(好比類對象)而言, malloc/free沒法自動執行對象的構造/析構函數.而new/delete卻能夠.
       malloc
函數malloc的原型:
void *malloc(size_t size);
       函數malloc的使用:
              int *p = (int*)malloc(sizeof(int)*length);//length前是乘號
       可見,在使用malloc時須要進行類型轉換.而使用sizeof運算符也是良好的代碼風格.
       new
new內置了sizeof,因此用起來寫法更簡潔.注意,使用new建立對象數組時,只能使用對象的無參數構造函數.如 Obj *o = new Obj[100];
 
 
7,內存耗盡怎麼辦?
解決辦法:
一、判斷指針是否爲NULL,若是是當即返回
void fun()
{
    A *a = new A();
    if(a==NULL)
           return;
}
二、判斷指針是否爲NULL,若是是當即終止
void fun()
{
    A *a = new A();
    if(a==NULL)
           exit(1);
}
提示:不要不忍心使用exit(1),不然會害死OS.因此推薦使用方法2.不過搞笑的是,在32位OS上,永遠也不會內存耗盡.
 
來自:http://blog.csdn.net/masterft/article/details/1711706
相關文章
相關標籤/搜索