C/C++程序的內存分配與使用筆記
一、C/C++程序的內存分配
一個C/C++程序佔用的內存區一般可以分爲如下五種:
①全局/靜態數據區
②常量數據區
③代碼區
④堆
⑤棧
顯然代碼存放在代碼區,而程序的數據則根據數據種類的不同放在不同的存儲區中,在C/C++中,數據主要有幾種不同的分類:常量和變量、全局數據和局部數據,靜態數據與非靜態數據,以及程序運行中產生和釋放的動態數據。
其中
①全局/靜態數據區中存儲全局變量及靜態變量(包括全局靜態變量和局部靜態變量);
②常量數據區中存儲程序中的各種常量;
③棧中存儲自動變量後者局部變量,以及傳遞的函數參數等
④堆是用戶控制的存儲區,存儲動態產生的數據(new或者malloc)。
下面通過一個簡單的實例來具體看看:
#include
#include
int g_nGlobal = 100;
int main()
{
char *pLocalString1 = "LocalString1";
const char *pLocalString2 = "LocalString2";
static int nLocalStatic = 0;
int nLocal = 0;
const int nLocalConst = 100;
int *pIntNew = new int[5];
char *pMalloc = (char *)malloc(1);
printf("global variable: 0x%x/n", &g_nGlobal);
printf("static variable: 0x%x/n", &nLocalStatic);
printf("local printer1: 0x%x/n", pLocalString1);
printf("local const printer: 0x%x/n/n", pLocalString2);
printf("new: 0x%x/n", pIntNew);
printf("malloc: 0x%x/n/n", pMalloc);
printf("local printer(pIntNew): 0x%x/n", &pIntNew);
printf("local printer(pLocalString1): 0x%x/n", &pLocalString1);
printf("local printer(pLocalString2): 0x%x/n", &pLocalString2);
printf("local variable(nLocal): 0x%x/n", &nLocal);
printf("local printer(pMalloc): 0x%x/n", &pMalloc);
printf("local const(nLocalConst): 0x%x/n", &nLocalConst);
delete []pIntNew;
free(pMalloc);
return 0;
}
實例共8個變量,包括一個全局變量g_nGlobal,1個局部靜態變量nLocalStatic、以及6個局部變量。
在Windows XP 中使用VC2008編譯運行,程序出處如下:
global variable: 0x417000
static variable: 0x417148
local printer1: 0x4158fc
local const printer: 0x4158ec
new: 0x383248
malloc: 0x383288
local printer(pIntNew): 0x12ff30
local printer(pLocalString1): 0x12ff60
local printer(pLocalString2): 0x12ff54
local variable(nLocal): 0x12ff48
local printer(pMalloc): 0x12ff24
local constvariable(nLocalConst): 0x12ff3c
從輸出結果可以看出程序數據的分配情況。
通過new分配的位於堆上,5個int共20個字節,由於在堆上位16字節對齊,所以佔用了32個字節從(48~88),內存對齊可是加速CPU對數據的訪問,但是同時也造成了空間浪費,C/C++中的class、union和struct,可以通過#pragma pack()或者配置編譯器來實現按要求對齊或者取消對齊。
程序的內存分配如下圖:
全局/靜態數據區 棧 堆
g_nGlobal pIntNew-->int[0..4]
nLocalStatic pMalloc--->char
… ….
"LocalString1"ß-------pLocalString1
"LocalString2"ß-------pLocalString2
nLocalConst
全局/靜態數據區是在程序編譯階段都已經分配好的,在整個程序運行過程中始終存在,用來保存全局變量、靜態變量、常量等。
其中字符串常量存儲區域的數據是不可以修改的
例如
char *pLocalString1 = "LocalString1";
pLocalString1[1]= 'a';//試圖修改不可修改的內存
二 堆和棧
雖然平常都是"堆棧堆棧"連着說的,但是他們的定義和作用是有區別的
在C/C++中,一個函數的內部變量以及傳遞給函數的參數等都是存儲在棧中的。當退出這些變量的作用域時,這些棧的內容會被彈出釋放。而是用malloc或者new申請的內存位於堆中,不會隨着變量作用域的結束而自動釋放,從而產生內存泄露。
例如實例中的
int *pIntNew = new int[5];
char *pMalloc = (char *)malloc(1);
pIntNew和pMalloc兩個變量本身是位於棧的,當main()結束退出時,會被自動釋放,而他們指向的內存在堆上,是不會自動釋放的,因此造成內存泄露,所以必須顯示的調用free或者delete。
這裏產生一個問題:既然棧上的內存會自動釋放不存在泄漏問題,而堆必須顯示釋放容易產生內存泄漏,爲什麼還要是用堆呢?
這是因爲很多應用需要動態的管理數據,例如鏈表,而當需要新增節點插入鏈表時,此時就需要在堆上申請內存並插入節點。而且棧的大小也是有限制的,佔用內存較多的對象只能在堆上分配。
除了以上的差別外,他們在大小和效率方面需要注意:
1、 大小
通常一個程序可以使用的棧的大小是固定的,由編譯器決定。例如vc2008棧的默認大小就是1MB,當然可以修改它的大小,但是通常都不會很大。
int buf[1024*1024];//運行時會出錯,棧溢出
但是堆的大小要比棧大很多,它主要受限與系統的虛擬內存的大小,可以分配比較大的數據。
2、效率
棧上的內存是系統自動分配的,pop與push都用相應的指令操作,因此效率比較高,而且分配的都是練習的內存空間,不會產生碎片。而堆上的內存是程序動態申請和釋放的,系統需要通過一定的算法在堆空間中尋找合適的空間再進行分配,並修改相應的維護堆空間的鏈表,再返回地址給程序,因此效率比棧低,而且容易產生碎片。