一、一個C++編譯的程序占用的內存分為以下幾個部分
1、棧區:由編譯器自動分配 存放函數的參數值,局部變量的值等,操作方式類似於數據結構中的棧。
2、堆區:一般由程序員分配釋放,若程序員不釋放,程序結束時 可能 有系統收回。它與數據結構中的堆是兩回事。分配方式類似於鏈表。
3、全局區(靜態區):全局變量和靜態變量是存儲放在一塊的,初始化的全局變量和靜態變量在一個區域,未初始化的在相鄰的另一個區域。
程序結束後由系統釋放。
4、文字常量區:常量字符串就存放在這裡。程序結束後有系統自動釋放。
5、程序代碼區:存放函數體的二進制代碼。
二、堆棧的理論知識
1、申請方式
棧:有系統自動分配。例如定義局部變量int i = 0;函數傳參時使用值傳遞。
堆:需要程序員自己申請並指明大小。如用malloc函數和new運算符。
2、申請後的系統響應
棧:只要棧的剩余空間大於所申請的空間,系統將為程序提供內存,負責報告棧溢出異常。
堆:這個設計到系統的內存管理,操作系統有一個記錄空閒內存地址的鏈表,然後根據系統的內存分配策略分配內存。
3、申請大小的限制
棧:在windows下,棧是向低地址擴展的數據結構,是一塊連續的內存區域。也就是說棧底和棧頂的地址和最大容量是
系統預先規定好的。在windows下據說是棧大小是2M,如果申請的空間超過棧的剩余空間時將提示溢出。因此棧的
空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區。因為系統用鏈表來存儲空閒內存的地址的,而鏈表遍歷的方向
是從低地址到高地址。堆的大小受限於計算機系統中有效地虛擬內存。獲得的空間比較靈活也比較大。
4、效率方面
堆:速度比較慢,容易產生碎片,不過用起來方便。在windows最快的是利用VirtualAlloc分配內存,他不在堆也不在棧中,
而是直接在棧的地址空間中保留一塊內存。使用起來速度快,靈活。
棧:速度快,不過由系統自動分配和控制。
5、存放內容方面
堆:一般是在堆的頭部用一個字節放堆的大小。堆中具體內容有程序員安排。
棧:在函數調用時第一個進棧的是主函數中下一條指令(函數執行語句的下一條可執行語句)地址,然後是各個函數的參數,
大多數C/C++編譯器中,函數參數是從右往左入棧,然後是函數中的局部變量。注意:靜態變量不如棧的。本次函數調
用結束後,局部變量先出棧,然後是函數參數,最後棧頂指針指向最開始存的主函數中下一條指令地址,程序由該點繼
續運行。
三、實例解說
//全局初始化區
int i1 = 0;
int i2 = 0;
int i3 = 0;
//全局初始化區
static int i4 = 0;
static int i5 = 0;
static int i6 = 0;
//全局未初始化區
int i7;
int i8;
int i9;
void Creat()
{
cout<<"Creat"<<endl;
}
void Add()
{
cout<<"Add"<<endl;
}
void Delete()
{
cout<<"Delete"<<endl;
}
int Max(int a,int b)//在調用此函數時參數從右往左開始壓棧
{
return a>b?a:b;
}
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"打印全局初始化區變量i1-i3的地址:"<<endl;
cout<<&i1<<" "<<&i2<<" "<<&i3<<endl;
cout<<"打印全局初始化區靜態變量i4-i6的地址:"<<endl;
cout<<&i4<<" "<<&i5<<" "<<&i6<<endl;
cout<<"打印全局未初始化區變量i7-i9的地址:"<<endl;
cout<<&i7<<" "<<&i8<<" "<<&i9<<endl;
cout<<"依次打印上面三個函數Creat、Add、Delete地址:"<<endl;
cout<<&Creat<<endl;
cout<<&Add<<endl;
cout<<&Delete<<endl;
//棧區
int c1 = 'a';
int c2 = 'b';
int c3;
int c4;
cout<<"打印主函數內局部變量c1-c4地址,其中c3,c4未初始化"<<endl;
cout<<&c1<<" "<<&c2<<" "<<&c3<<" "<<&c4<<" "<<endl;
char *pStr1 = "12345";//12345在常量區,pStr在棧上
char *pStr2 = "1122";
void *p = pStr1;
void *q = pStr2;
cout<<"打印常量地址"<<endl;
cout<<p<<endl;
cout<<q<<endl;
static int i10 = 0;//全局(靜態)初始化區
cout<<"在局部函數中定義靜態變量地址,請於上面答應的其他全局區地址作比較"<<endl;
cout<<&i10<<endl;
int *p1 = new int;//堆區
int *p2 = new int;//堆區
cout<<"打印堆區地址"<<endl;
cout<<p1<<endl;
cout<<p2<<endl;
strcpy(pStr1,"1144");//12345在常量區,編譯器可能將pStr1文字常量1144優化成一個地方
getchar();
return 0;
}
輸出:
以上結果在VS2008中測試。對上面結果地址觀察發現,全局未初始化區的變量是按從高到低地址按申明定義的
順序壓棧,變量i7緊鄰全局初始化段的第一個變量i1.而全局初始化段的變量(包括靜態,不做區分的)從低地址
到高地址按申明的順序壓棧(不是指上面所指的棧區,請區別開來,這是就地址變化過程而言的,你會看到它與
局部函數變量起始地址完全不同)。函數在程序代碼段中地址是按申明順序遞增的。函數局部變量在棧去是按照
申明順序從高到低的地址進棧的。這裡看到我定義的幾個int變量地址相差是12個字節還不清楚是不是編譯器原因。
常量的開始地址來看跟全局變量應該屬於一個區。堆區的地址開頭也是另外一個段。
補充一點:數組變量內部元素是按照元素下標從低地址到高地址壓棧的。
一般局部變量一般是從高低地址到低地址壓棧的。
從上面結果來看全局變量實際可能在堆區。
以上內容主要參考http://blog.csdn.net/benny5609/article/details/2217258原創文章
生命在於折騰,生活就是如此的豐富多彩