C++程序的五大內(nèi)存分區(qū)實(shí)例詳解
C++程序在運(yùn)行時(shí)所占用的內(nèi)存區(qū)域,一般可分為棧內(nèi)存區(qū)、堆內(nèi)存區(qū)、全局/靜態(tài)內(nèi)存區(qū)、文字常量?jī)?nèi)存區(qū)及程序代碼區(qū)5大分區(qū):
下面使用日常開(kāi)發(fā)中的編程實(shí)例,詳細(xì)介紹一下這5個(gè)分區(qū),以便大家能更深刻的理解這5大內(nèi)存分區(qū)。
1、棧內(nèi)存區(qū)
棧內(nèi)存區(qū)是我們用的最多的分區(qū),只要有函數(shù)的地方都會(huì)使用到這個(gè)分區(qū)。棧分區(qū)是用來(lái)存放函數(shù)參數(shù)及函數(shù)局部變量值的內(nèi)存區(qū),是由編譯器在編譯時(shí)自動(dòng)分配和釋放的。
函數(shù)中的參數(shù)與函數(shù)中的局部變量占用的內(nèi)存是代碼執(zhí)行到函數(shù)(進(jìn)入函數(shù))是分配的,在離開(kāi)時(shí)函數(shù)時(shí)這些內(nèi)存會(huì)自動(dòng)被釋放。下面從下面幾個(gè)簡(jiǎn)單的實(shí)例來(lái)更進(jìn)一步地認(rèn)識(shí)棧內(nèi)存。
1.1、調(diào)用函數(shù)時(shí)通過(guò)棧來(lái)傳遞函數(shù)的參數(shù)值
調(diào)用函數(shù)時(shí)時(shí)通過(guò)棧傳遞參數(shù)值的,即在調(diào)用函數(shù)之前要將函數(shù)的參數(shù)值依次壓入到棧上,然后再去call被調(diào)用函數(shù)的。這點(diǎn)從匯編代碼上可以清晰地看出來(lái)。比如下面一段簡(jiǎn)單的實(shí)現(xiàn)兩數(shù)相加的代碼:
// 被調(diào)用函數(shù) int AddNum(int a, int b) { int nSum = a + b; return nSum; } // 調(diào)用內(nèi)調(diào)用函數(shù)的實(shí)例代碼 int a = 7; int b = 8; int nSum = AddNum(a, b);
可以在VS中查看上述C++代碼對(duì)應(yīng)的匯編代碼。具體的做法是,將上述代碼拷貝到VS中,啟動(dòng)VS調(diào)試,在鼠標(biāo)右鍵單擊顯示的右鍵菜單中點(diǎn)擊“轉(zhuǎn)到反匯編”區(qū)查看C++代碼對(duì)應(yīng)的匯編代碼:
從上述匯編代碼可以看出,在調(diào)用AddNum函數(shù)之前,將要傳入的參數(shù)a和參數(shù)b的值先壓到棧上,然后再去call AddNum函數(shù)。作為被調(diào)函數(shù)的AddNum會(huì)從棧上讀取傳入的參數(shù)內(nèi)容。
1.2、線(xiàn)程占用的棧內(nèi)存是有上限的
線(xiàn)程占用的棧內(nèi)存是有上限的,可以在創(chuàng)建線(xiàn)程時(shí)指定棧空間的大小。在Windows上,線(xiàn)程默認(rèn)的棧空間是1MB。線(xiàn)程在某一時(shí)刻的函數(shù)調(diào)用堆棧中的所有函數(shù)占用的??臻g總和,就是當(dāng)前時(shí)刻的線(xiàn)程占用的棧內(nèi)存。
進(jìn)入函數(shù)時(shí)會(huì)將該函數(shù)的棧空間累計(jì)到所在線(xiàn)程的??臻g占用內(nèi)存數(shù)上(函數(shù)內(nèi)部申請(qǐng)存放局部變量的棧空間),離開(kāi)函數(shù)則會(huì)釋放它占用的??臻g,就會(huì)將所在線(xiàn)程占用的棧內(nèi)存數(shù)上減掉函該函數(shù)占用的空間。如果當(dāng)前線(xiàn)程占用的??臻g大于線(xiàn)程的上限時(shí)(一般是在進(jìn)入一個(gè)函數(shù)時(shí)觸發(fā)),則會(huì)報(bào)出“stack overflow”的棧溢出異常:
程序會(huì)發(fā)生崩潰。
這里有一點(diǎn)需要說(shuō)明一下,在某個(gè)函數(shù)中使用了switch...case語(yǔ)句,語(yǔ)句中包含了多個(gè)case分支,在這些分支中定義了一些局部變量,雖然這些局部變量的生命周期只位于case子句中,但是都是直接算在所在函數(shù)的??臻g上的,是剛執(zhí)行進(jìn)函數(shù)就分配好了,即便當(dāng)前還沒(méi)運(yùn)行到對(duì)應(yīng)的case子句中,即便這些case子句的局部變量的生命周期僅在case子句內(nèi)!
2、堆內(nèi)存區(qū)
堆內(nèi)存也是我們最常用的內(nèi)存區(qū),因?yàn)槊總€(gè)線(xiàn)程的棧內(nèi)存是有限的,我們一般將大部分?jǐn)?shù)據(jù)要放置在堆內(nèi)存中。
在C++中,malloc/new申請(qǐng)的內(nèi)存都是從堆內(nèi)存上分配的,用完后由free/delete區(qū)釋放的。如果沒(méi)有釋放堆內(nèi)存,則程序結(jié)束時(shí)由操作系統(tǒng)統(tǒng)一回收。
堆內(nèi)存的管理比棧內(nèi)存要復(fù)雜的多,如果是堆內(nèi)存異常導(dǎo)致的崩潰,比棧內(nèi)存異常(比如內(nèi)存越界引起內(nèi)存訪(fǎng)問(wèn)為例)導(dǎo)致的崩潰,要難查的多。
如果malloc/new來(lái)的內(nèi)存在用完后沒(méi)有釋放,則會(huì)導(dǎo)致內(nèi)存泄露,如果頻繁執(zhí)行的代碼中有內(nèi)存泄露則是致命的,因?yàn)殡S著程序的運(yùn)行時(shí)間的加長(zhǎng),會(huì)產(chǎn)生越來(lái)越多的內(nèi)存泄露,如果將所屬進(jìn)程虛擬內(nèi)存耗盡,會(huì)產(chǎn)生“Out of memory”的異常:
程序直接閃退崩潰。
3、全局/靜態(tài)內(nèi)存區(qū)
全局變量和靜態(tài)變量的內(nèi)存就是在該區(qū)上分配的,全局變量和靜態(tài)變量的生命周期也是一樣的,都是在程序啟動(dòng)時(shí)分配內(nèi)存的,在程序退出時(shí)釋放內(nèi)存的。
全局變量一般會(huì)使用extern關(guān)鍵字來(lái)聲明,比如:
extern int m_nClientId;
而靜態(tài)變量則是使用static關(guān)鍵字來(lái)聲明:
static int nCount;
全局變量和靜態(tài)變量都要求在定義的時(shí)候要初始化,注意此處講的定義是和聲明是相對(duì)應(yīng)的概念。全局變量和靜態(tài)變量的區(qū)別在于,全局變量的作用域更廣,整個(gè)模塊中都能使用。靜態(tài)變量則因其定義的位置不同有不同的作用域。
可以在函數(shù)中定義靜態(tài)變量,也可以在類(lèi)中定義靜態(tài)成員變量。函數(shù)中定義的靜態(tài)變量只能在函數(shù)中被訪(fǎng)問(wèn),類(lèi)中定義的靜態(tài)變量則可以在類(lèi)外部使用“類(lèi)名::靜態(tài)成員變量名”去訪(fǎng)問(wèn)。
4、文字常量區(qū)
該分區(qū)是用來(lái)存放常量值,如常量字符串等,比如如下的字符串常量:(將字符串常量的地址賦值給指針p):
char* p = ”this is a test.”;
該字符串占用的內(nèi)存地址就是文字常量區(qū)內(nèi)存上的。
該部分內(nèi)存中的內(nèi)容是固定的常量,是不允許修改的,程序結(jié)束后由操作系統(tǒng)統(tǒng)一回收。這部分內(nèi)容比較簡(jiǎn)單,沒(méi)什么要講的。
5、程序代碼區(qū)
前面說(shuō)的內(nèi)存都是數(shù)據(jù)段的內(nèi)存,是用來(lái)存放程序運(yùn)行中的各種數(shù)據(jù)的;該部分的內(nèi)存是代碼段的內(nèi)存,是用來(lái)存放程序二進(jìn)制代碼的。
數(shù)據(jù)段的內(nèi)存地址和代碼段指令的地址,完全是兩個(gè)概念,不能混為一談。比如某個(gè)變量的內(nèi)存地址是數(shù)據(jù)段的地址:
某條匯編指令的地址,則是代碼段的地址:
是兩個(gè)完全不搭嘎的地址,一定要區(qū)分開(kāi)來(lái)。
到此這篇關(guān)于C++程序的五大內(nèi)存分區(qū)實(shí)例詳解的文章就介紹到這了,更多相關(guān)C++ 五大內(nèi)存分區(qū)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Typedef在C語(yǔ)言和C++中的用法和區(qū)別
在C語(yǔ)言和C++中,typedef是一個(gè)非常常用的關(guān)鍵字,用于為數(shù)據(jù)類(lèi)型定義別名,盡管它在兩種語(yǔ)言中都有相似的功能,但由于C++具有更豐富的類(lèi)型系統(tǒng),因此在實(shí)際應(yīng)用中,typedef在兩者間的使用存在一些微妙的差異2024-01-01C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配實(shí)例詳解
動(dòng)態(tài)內(nèi)存管理同時(shí)還具有一個(gè)優(yōu)點(diǎn),當(dāng)程序在具有更多內(nèi)存的系統(tǒng)上需要處理更多數(shù)據(jù)時(shí),不需要重寫(xiě)程序,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的相關(guān)資料,需要的朋友可以參考下2022-06-06C語(yǔ)言連續(xù)生成隨機(jī)數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了C語(yǔ)言連續(xù)生成隨機(jī)數(shù)的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01OpenCV計(jì)算輪廓長(zhǎng)度/周長(zhǎng)和面積
這篇文章主要為大家詳細(xì)介紹了OpenCV計(jì)算輪廓長(zhǎng)度/周長(zhǎng)和面積,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06OpenMP?Parallel?Construct的實(shí)現(xiàn)原理詳解
在本篇文章當(dāng)中我們將主要分析?OpenMP?當(dāng)中的?parallel?construct?具體時(shí)如何實(shí)現(xiàn)的,以及這個(gè)?construct?調(diào)用了哪些運(yùn)行時(shí)庫(kù)函數(shù),并且詳細(xì)分析這期間的參數(shù)傳遞,需要的可以參考一下2023-01-01一個(gè)string類(lèi)的簡(jiǎn)單實(shí)現(xiàn)案例
下面小編就為大家?guī)?lái)一篇一個(gè)string類(lèi)的簡(jiǎn)單實(shí)現(xiàn)案例。小編覺(jué)得挺不錯(cuò)的現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01一篇文章帶你實(shí)現(xiàn)C語(yǔ)言中常用庫(kù)函數(shù)的模擬
這篇文章主要介紹了C語(yǔ)言中常用庫(kù)函數(shù)的模擬,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09C語(yǔ)言實(shí)現(xiàn)酒店管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)酒店管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06VSCode斷點(diǎn)調(diào)試CMake工程項(xiàng)目的實(shí)現(xiàn)步驟
這篇文章主要介紹了VSCode斷點(diǎn)調(diào)試CMake工程項(xiàng)目的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03C語(yǔ)言實(shí)現(xiàn)五子棋對(duì)戰(zhàn)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)五子棋對(duì)戰(zhàn)系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05