C++程序的五大內存分區實力詳解
C++程序在運行時所占用的內存區域,一般可分為棧內存區、堆內存區、全局/靜態內存區、文字常量內存區及程序代碼區5大分區:
下面使用日常開發中的編程實例,詳細介紹一下這5個分區,以便大傢能更深刻的理解這5大內存分區。
1、棧內存區
棧內存區是我們用的最多的分區,隻要有函數的地方都會使用到這個分區。棧分區是用來存放函數參數及函數局部變量值的內存區,是由編譯器在編譯時自動分配和釋放的。
函數中的參數與函數中的局部變量占用的內存是代碼執行到函數(進入函數)是分配的,在離開時函數時這些內存會自動被釋放。下面從下面幾個簡單的實例來更進一步地認識棧內存。
1.1、調用函數時通過棧來傳遞函數的參數值
調用函數時時通過棧傳遞參數值的,即在調用函數之前要將函數的參數值依次壓入到棧上,然後再去call被調用函數的。這點從匯編代碼上可以清晰地看出來。比如下面一段簡單的實現兩數相加的代碼:
// 被調用函數 int AddNum(int a, int b) { int nSum = a + b; return nSum; } // 調用內調用函數的實例代碼 int a = 7; int b = 8; int nSum = AddNum(a, b);
可以在VS中查看上述C++代碼對應的匯編代碼。具體的做法是,將上述代碼拷貝到VS中,啟動VS調試,在鼠標右鍵單擊顯示的右鍵菜單中點擊“轉到反匯編”區查看C++代碼對應的匯編代碼:
從上述匯編代碼可以看出,在調用AddNum函數之前,將要傳入的參數a和參數b的值先壓到棧上,然後再去call AddNum函數。作為被調函數的AddNum會從棧上讀取傳入的參數內容。
1.2、線程占用的棧內存是有上限的
線程占用的棧內存是有上限的,可以在創建線程時指定棧空間的大小。在Windows上,線程默認的棧空間是1MB。線程在某一時刻的函數調用堆棧中的所有函數占用的棧空間總和,就是當前時刻的線程占用的棧內存。
進入函數時會將該函數的棧空間累計到所在線程的棧空間占用內存數上(函數內部申請存放局部變量的棧空間),離開函數則會釋放它占用的棧空間,就會將所在線程占用的棧內存數上減掉函該函數占用的空間。如果當前線程占用的棧空間大於線程的上限時(一般是在進入一個函數時觸發),則會報出“stack overflow”的棧溢出異常:
程序會發生崩潰。
這裡有一點需要說明一下,在某個函數中使用瞭switch…case語句,語句中包含瞭多個case分支,在這些分支中定義瞭一些局部變量,雖然這些局部變量的生命周期隻位於case子句中,但是都是直接算在所在函數的棧空間上的,是剛執行進函數就分配好瞭,即便當前還沒運行到對應的case子句中,即便這些case子句的局部變量的生命周期僅在case子句內!
2、堆內存區
堆內存也是我們最常用的內存區,因為每個線程的棧內存是有限的,我們一般將大部分數據要放置在堆內存中。
在C++中,malloc/new申請的內存都是從堆內存上分配的,用完後由free/delete區釋放的。如果沒有釋放堆內存,則程序結束時由操作系統統一回收。
堆內存的管理比棧內存要復雜的多,如果是堆內存異常導致的崩潰,比棧內存異常(比如內存越界引起內存訪問為例)導致的崩潰,要難查的多。
如果malloc/new來的內存在用完後沒有釋放,則會導致內存泄露,如果頻繁執行的代碼中有內存泄露則是致命的,因為隨著程序的運行時間的加長,會產生越來越多的內存泄露,如果將所屬進程虛擬內存耗盡,會產生“Out of memory”的異常:
程序直接閃退崩潰。
3、全局/靜態內存區
全局變量和靜態變量的內存就是在該區上分配的,全局變量和靜態變量的生命周期也是一樣的,都是在程序啟動時分配內存的,在程序退出時釋放內存的。
全局變量一般會使用extern關鍵字來聲明,比如:
extern int m_nClientId;
而靜態變量則是使用static關鍵字來聲明:
static int nCount;
全局變量和靜態變量都要求在定義的時候要初始化,註意此處講的定義是和聲明是相對應的概念。全局變量和靜態變量的區別在於,全局變量的作用域更廣,整個模塊中都能使用。靜態變量則因其定義的位置不同有不同的作用域。
可以在函數中定義靜態變量,也可以在類中定義靜態成員變量。函數中定義的靜態變量隻能在函數中被訪問,類中定義的靜態變量則可以在類外部使用“類名::靜態成員變量名”去訪問。
4、文字常量區
該分區是用來存放常量值,如常量字符串等,比如如下的字符串常量:(將字符串常量的地址賦值給指針p):
char* p = ”this is a test.”;
該字符串占用的內存地址就是文字常量區內存上的。
該部分內存中的內容是固定的常量,是不允許修改的,程序結束後由操作系統統一回收。這部分內容比較簡單,沒什麼要講的。
5、程序代碼區
前面說的內存都是數據段的內存,是用來存放程序運行中的各種數據的;該部分的內存是代碼段的內存,是用來存放程序二進制代碼的。
數據段的內存地址和代碼段指令的地址,完全是兩個概念,不能混為一談。比如某個變量的內存地址是數據段的地址:
某條匯編指令的地址,則是代碼段的地址:
是兩個完全不搭嘎的地址,一定要區分開來。
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!