內(nèi)存分配——棧、堆、靜態(tài)區(qū)唆铐、符號區(qū)等等

一個由C/C++編譯的程序占用的內(nèi)存分為以下幾個部分

1哲戚、棧區(qū)(stack)—由編譯器自動分配釋放,存放函數(shù)參數(shù)值艾岂,局部變量的值等顺少。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

2澳盐、堆區(qū)(heap) — 一般由程序員分配釋放祈纯, 若程序員不釋放,程序結(jié)束時可能由OS回收 叼耙。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事腕窥,分配方式倒是類似于鏈表。

3筛婉、全局區(qū)(靜態(tài)區(qū))(static)—簇爆,全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域爽撒, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域入蛆。 -程序結(jié)束后由系統(tǒng)釋放

4、文字常量區(qū) —常量字符串就是放在這里的硕勿。程序結(jié)束后由系統(tǒng)釋放

5哨毁、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼。

二源武、例子程序

這是一個前輩寫的扼褪,非常詳細(xì)

//main.cpp

int a = 0; 全局初始化區(qū)

char *p1; 全局未初始化區(qū)

main()

{

int b; 棧

char s[] = "abc"; 棧

char *p2; 棧

char *p3 = "123456"; 123456在常量區(qū),p3在棧上粱栖。

static int c =0话浇; 全局(靜態(tài))初始化區(qū)

p1 = (char *)malloc(10);

p2 = (char *)malloc(20);

分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。

strcpy(p1, "123456"); 123456放在常量區(qū)闹究,編譯器可能會將它與p3所指向的"123456"優(yōu)化成一個地方幔崖。

}

二、堆和棧的理論知識

2.1申請方式

stack:

由系統(tǒng)自動分配渣淤。 例如赏寇,聲明在函數(shù)中一個局部變量 int b; 系統(tǒng)自動在棧中為b開辟空間

heap:

需要程序員自己申請,并指明大小价认,在c中malloc函數(shù)

如p1 = (char *)malloc(10);

在C++中用new運算符

如p2 = (char *)malloc(10);

但是注意p1蹋订、p2本身是在棧中的。

2.2

申請后系統(tǒng)的響應(yīng)

棧:只要棧的剩余空間大于所申請空間刻伊,系統(tǒng)將為程序提供內(nèi)存露戒,否則將報異常提示棧溢出椒功。

堆:首先應(yīng)該知道操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請時智什,

會遍歷該鏈表动漾,尋找第一個空間大于所申請空間的堆結(jié)點,然后將該結(jié)點從空閑結(jié)點鏈表中刪除荠锭,并將該結(jié)點的空間分配給程序旱眯,另外,對于大多數(shù)系統(tǒng)证九,會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小删豺,這樣,代碼中的 delete語句才能正確的釋放本內(nèi)存空間愧怜。另外呀页,由于找到的堆結(jié)點的大小不一定正好等于申請的大小,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中拥坛。

2.3申請大小的限制

棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)蓬蝶,是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的猜惋,在 WINDOWS下丸氛,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數(shù))著摔,如果申請的空間超過棧的剩余空間時缓窜,將提示overflow。因此谍咆,能從棧獲得的空間較小禾锤。

堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域卧波。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的庇茫,而鏈表的遍歷方向是由低地址向高地址港粱。堆的大小受限于計算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見旦签,堆獲得的空間比較靈活查坪,也比較大。

2.4申請效率的比較:

棧由系統(tǒng)自動分配宁炫,速度較快偿曙。但程序員是無法控制的。

堆是由new分配的內(nèi)存羔巢,一般速度比較慢望忆,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便.

另外罩阵,在WINDOWS下,最好的方式是用VirtualAlloc分配內(nèi)存启摄,他不是在堆稿壁,也不是在棧是直接在進(jìn)程的地址空間中保留一快內(nèi)存,雖然用起來最不方便歉备。但是速度快傅是,也最靈活。

2.5堆和棧中的存儲內(nèi)容

棧: 在函數(shù)調(diào)用時蕾羊,第一個進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址喧笔,然后是函數(shù)的各個參數(shù),在大多數(shù)的C編譯器中龟再,參數(shù)是由右往左入棧的书闸,然后是函數(shù)中的局部變量。注意靜態(tài)變量是不入棧的吸申。

當(dāng)本次函數(shù)調(diào)用結(jié)束后梗劫,局部變量先出棧,然后是參數(shù)截碴,最后棧頂指針指向最開始存的地址梳侨,也就是主函數(shù)中的下一條指令,程序由該點繼續(xù)運行日丹。

堆:一般是在堆的頭部用一個字節(jié)存放堆的大小走哺。堆中的具體內(nèi)容有程序員安排。

2.6存取效率的比較

char s1[] = "aaaaaaaaaaaaaaa";

char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa 是在運行時刻賦值的哲虾;

而bbbbbbbbbbb是在編譯時就確定的丙躏;

但是,在以后的存取中束凑,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快晒旅。

比如:

#include

void main()

{

char a = 1;

char c[] = "1234567890";

char *p ="1234567890";

a = c[1];

a = p[1];

return;

}

對應(yīng)的匯編代碼

10: a = c[1];

00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]

0040106A 88 4D FC mov byte ptr [ebp-4],cl

11: a = p[1];

0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]

00401070 8A 42 01 mov al,byte ptr [edx+1]

00401073 88 45 FC mov byte ptr [ebp-4],al

第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中汪诉,在根據(jù)edx讀取字符废恋,顯然慢了。

2.7小結(jié):

堆和棧的區(qū)別可以用如下的比喻來看出:

使用棧就象我們?nèi)ワ堭^里吃飯扒寄,只管點菜(發(fā)出申請)鱼鼓、付錢、和吃(使用)该编,吃飽了就走迄本,不必理會切菜、洗菜等準(zhǔn)備工作和洗碗课竣、刷鍋等掃尾工作嘉赎,他的好處是快捷置媳,但是自由度小。

使用堆就象是自己動手做喜歡吃的菜肴曹阔,比較麻煩半开,但是比較符合自己的口味,而且自由度大赃份。

1寂拆、內(nèi)存分配方面:

堆:一般由程序員分配釋放, 若程序員不釋放抓韩,程序結(jié)束時可能由OS回收 纠永。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式是類似于鏈表谒拴〕⒔可能用到的關(guān)鍵字如下:new、malloc英上、delete炭序、free等等。

棧:由編譯器(Compiler)自動分配釋放苍日,存放函數(shù)的參數(shù)值惭聂,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧相恃。

2辜纲、申請方式方面:

堆:需要程序員自己申請,并指明大小拦耐。在c中malloc函數(shù)如p1 = (char *)malloc(10)耕腾;在C++中用new運算符,但是注意p1杀糯、p2本身是在棧中的扫俺。因為他們還是可以認(rèn)為是局部變量。

棧:由系統(tǒng)自動分配固翰。 例如狼纬,聲明在函數(shù)中一個局部變量 int b;系統(tǒng)自動在棧中為b開辟空間倦挂。

3畸颅、系統(tǒng)響應(yīng)方面:

堆:操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表担巩,當(dāng)系統(tǒng)收到程序的申請時方援,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結(jié)點涛癌,然后將該結(jié)點從空閑結(jié)點鏈表中刪除犯戏,并將該結(jié)點的空間分配給程序,另外,對于大多數(shù)系統(tǒng)泊碑,會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小你弦,這樣代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外由于找到的堆結(jié)點的大小不一定正好等于申請的大小呀非,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中坚俗。

棧:只要棧的剩余空間大于所申請空間,系統(tǒng)將為程序提供內(nèi)存岸裙,否則將報異常提示棧溢出猖败。

4、大小限制方面:

堆:是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)降允,是不連續(xù)的內(nèi)存區(qū)域恩闻。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的剧董,而鏈表的遍歷方向是由低地址向高地址幢尚。堆的大小受限于計算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見翅楼,堆獲得的空間比較靈活尉剩,也比較大。

棧:在Windows下, 棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)犁嗅,是一塊連續(xù)的內(nèi)存的區(qū)域边涕。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在WINDOWS下褂微,棧的大小是固定的(是一個編譯時就確定的常數(shù))功蜓,如果申請的空間超過棧的剩余空間時,將提示overflow宠蚂。因此式撼,能從棧獲得的空間較小。

5求厕、效率方面:

堆:是由new分配的內(nèi)存著隆,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片呀癣,不過用起來最方便美浦,另外,在WINDOWS下项栏,最好的方式是用 VirtualAlloc分配內(nèi)存浦辨,他不是在堆,也不是在棧是直接在進(jìn)程的地址空間中保留一快內(nèi)存沼沈,雖然用起來最不方便流酬。但是速度快币厕,也最靈活。

棧:由系統(tǒng)自動分配芽腾,速度較快旦装。但程序員是無法控制的。

6摊滔、存放內(nèi)容方面:

堆:一般是在堆的頭部用一個字節(jié)存放堆的大小阴绢。堆中的具體內(nèi)容有程序員安排。

棧:在函數(shù)調(diào)用時第一個進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址然后是函數(shù)的各個參數(shù)艰躺,在大多數(shù)的C編譯器中旱函,參數(shù)是由右往左入棧,然后是函數(shù)中的局部變量描滔。 注意: 靜態(tài)變量是不入棧的棒妨。當(dāng)本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧含长,然后是參數(shù)券腔,最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令拘泞,程序由該點繼續(xù)運行纷纫。

7、存取效率方面:

堆:char *s1 = "Hellow Word"陪腌;是在編譯時就確定的辱魁;

棧:char s1[] = "Hellow Word"; 是在運行時賦值的诗鸭;用數(shù)組比用指針?biāo)俣纫煲恍┤敬兀驗橹羔樤诘讓訁R編中需要用edx寄存器中轉(zhuǎn)一下,而數(shù)組在棧上直接讀取强岸。

C語言內(nèi)存管理

【規(guī)則1】用malloc或new申請內(nèi)存之后锻弓,應(yīng)該立即檢查指針值是否為NULL。防止使用指針值為NULL的內(nèi)存蝌箍。

【規(guī)則2】不要忘記為數(shù)組和動態(tài)內(nèi)存賦初值青灼。防止將未被初始化的內(nèi)存作為右值使用。

【規(guī)則3】避免數(shù)組或指針的下標(biāo)越界妓盲,特別要當(dāng)心發(fā)生“多1”或者“少1”操作杂拨。

【規(guī)則4】動態(tài)內(nèi)存的申請與釋放必須配對,防止內(nèi)存泄漏悯衬。

【規(guī)則5】用free或delete釋放了內(nèi)存之后弹沽,立即將指針設(shè)置為NULL,防止產(chǎn)生“野指針”。

常見的內(nèi)存錯誤及其對策如下:

1.內(nèi)存分配未成功贷币,卻使用了它。

編程新手常犯這種錯誤亏狰,因為他們沒有意識到內(nèi)存分配會不成功役纹。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL暇唾。如果指針p是函數(shù)的參數(shù)促脉,那么在函數(shù)的入口處用assert(p!=NULL)進(jìn)行檢查。如果是用malloc或new來申請內(nèi)存策州,應(yīng)該用if(p==NULL)或if(p!=NULL)進(jìn)行防錯處理瘸味。

2.內(nèi)存分配雖然成功,但是尚未初始化就引用它够挂。

犯這種錯誤主要有兩個起因:一是沒有初始化的觀念旁仿;二是誤以為內(nèi)存的缺省初值全為零,導(dǎo)致引用初值錯誤(例如數(shù)組)孽糖。

內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)一的標(biāo)準(zhǔn)枯冈,盡管有些時候為零值,我們寧可信其無不可信其有办悟。所以無論用何種方式創(chuàng)建數(shù)組尘奏,都別忘了賦初值,即便是賦零值也不可省略病蛉,不要嫌麻煩炫加。

3.內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界铺然。

例如在使用數(shù)組時經(jīng)常發(fā)生下標(biāo)“多1”或者“少1”的操作俗孝。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯魄健,導(dǎo)致數(shù)組操作越界驹针。

4.忘記了釋放內(nèi)存,造成內(nèi)存泄露诀艰。

含有這種錯誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存柬甥。剛開始時系統(tǒng)的內(nèi)存充足,你看不到錯誤其垄。終有一次程序突然死掉苛蒲,系統(tǒng)出現(xiàn)提示:內(nèi)存耗盡。

動態(tài)內(nèi)存的申請與釋放必須配對绿满,程序中malloc與free的使用次數(shù)一定要相同臂外,否則肯定有錯誤(new/delete同理)。

5.釋放了內(nèi)存卻繼續(xù)使用它。

有三種情況:

(1)程序中的對象調(diào)用關(guān)系過于復(fù)雜漏健,實在難以搞清楚某個對象究竟是否已經(jīng)釋放了內(nèi)存嚎货,此時應(yīng)該重新設(shè)計數(shù)據(jù)結(jié)構(gòu),從根本上解決對象管理的混亂局面蔫浆。

(2)函數(shù)的return語句寫錯了殖属,注意不要返回指向“棧內(nèi)存”的“指針”或者“引用”,因為該內(nèi)存在函數(shù)體結(jié)束時被自動銷毀瓦盛。

(3)使用free或delete釋放了內(nèi)存后洗显,沒有將指針設(shè)置為NULL。導(dǎo)致產(chǎn)生“野指針”


原文地址:?內(nèi)存分配——棧原环、堆挠唆、靜態(tài)區(qū)、符號區(qū)等等

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嘱吗,一起剝皮案震驚了整個濱河市玄组,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谒麦,老刑警劉巖巧勤,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弄匕,居然都是意外死亡颅悉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門迁匠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剩瓶,“玉大人,你說我怎么就攤上這事城丧⊙邮铮” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵亡哄,是天一觀的道長枝缔。 經(jīng)常有香客問我,道長蚊惯,這世上最難降的妖魔是什么愿卸? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮截型,結(jié)果婚禮上趴荸,老公的妹妹穿的比我還像新娘。我一直安慰自己宦焦,他們只是感情好发钝,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布顿涣。 她就那樣靜靜地躺著,像睡著了一般酝豪。 火紅的嫁衣襯著肌膚如雪涛碑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天孵淘,我揣著相機(jī)與錄音蒲障,去河邊找鬼。 笑死夺英,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的滋捶。 我是一名探鬼主播痛悯,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼重窟!你這毒婦竟也來了载萌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤巡扇,失蹤者是張志新(化名)和其女友劉穎扭仁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厅翔,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡乖坠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了刀闷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熊泵。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甸昏,靈堂內(nèi)的尸體忽然破棺而出顽分,到底是詐尸還是另有隱情,我是刑警寧澤施蜜,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布卒蘸,位于F島的核電站,受9級特大地震影響翻默,放射性物質(zhì)發(fā)生泄漏缸沃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一修械、第九天 我趴在偏房一處隱蔽的房頂上張望和泌。 院中可真熱鬧,春花似錦祠肥、人聲如沸武氓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽县恕。三九已至东羹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忠烛,已是汗流浹背属提。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留美尸,地道東北人冤议。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像师坎,于是被迫代替她去往敵國和親恕酸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容