【C语言天天练(二四)】内存分配
引言:
? ? ??
? ? ? ? 對于C語言程序,了解它執(zhí)行時在內(nèi)存中是怎樣分配的對于我們理解它的執(zhí)行機(jī)制是很實用的。以下就總結(jié)一下C語言程序的一些內(nèi)存分配知識。
一
? ? ? ? 一段C程序。編譯連接后形成的可運(yùn)行文件一般有代碼段、數(shù)據(jù)段、堆和棧等幾部分組成。當(dāng)中數(shù)據(jù)段又包含僅僅讀數(shù)據(jù)段、已初始化的讀寫數(shù)據(jù)段和未初始化的BSS段。例如以下圖所看到的:
文本段:存放程序運(yùn)行的代碼。
數(shù)據(jù)段:
1>僅僅讀數(shù)據(jù)段:
僅僅讀數(shù)據(jù)段是程序使用的一些不會被更改的數(shù)據(jù),使用這些數(shù)據(jù)的方式類似查表式的操作,因為這些變量不須要更改,因此僅僅須要放置在僅僅讀存儲器中就可以。
通常是const修飾的變量以及程序中使用的文字常量通常會存放在僅僅讀數(shù)據(jù)段中。
2>已初始化的讀寫數(shù)據(jù)段:
已初始化數(shù)據(jù)是在程序中聲明,而且具有初值的變量。這些變量須要占用存儲器的空間,在程序執(zhí)行時它們須要位于可讀寫的內(nèi)存區(qū)域內(nèi)。而且有初值,以供程序執(zhí)行時讀寫。在程序中一般為已經(jīng)初始化的全局變量,已經(jīng)初始化的靜態(tài)局部變量(static修飾的已經(jīng)初始化的變量)
3>未初始化段(BSS):
未初始化數(shù)據(jù)是在程序中聲明,可是沒有初始化的變量,這些變量在程序執(zhí)行之前不須要占用存儲器的空間。
與讀寫數(shù)據(jù)段類似,它也屬于靜態(tài)數(shù)據(jù)區(qū)。
可是該段中數(shù)據(jù)沒有經(jīng)過初始化。
未初始化數(shù)據(jù)段僅僅有在執(zhí)行的初始化階段才會產(chǎn)生,因此它的大小不會影響目標(biāo)文件的大小。在程序中通常是沒有初始化的全局變量和沒有初始化的靜態(tài)局部變量。
棧:由系統(tǒng)自己主動分配。比如,聲明在函數(shù)中一個局部變量int b;系統(tǒng)自己主動在棧中為b開辟空間。
二
依據(jù)上面的理論知識,分析演示樣例片段的內(nèi)存分配:
三
棧與堆的差別:
1.申請方式
(1)棧(satck):由系統(tǒng)自己主動分配。比如,聲明在函數(shù)中一個局部變量int b;系統(tǒng)自己主動在棧中為b開辟空間。
(2)堆(heap):需程序猿自己申請(調(diào)用malloc,realloc,calloc),并指明大小,并由程序猿進(jìn)行釋放。
easy產(chǎn)生memory leak.
eg:char? p;
????? p = (char *)malloc(sizeof(char));
可是,p本身是在棧中。
2.申請大小的限制
(1)棧:棧是向底地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存區(qū)域(它的生長方向與內(nèi)存的生長方向相反)。棧的大小是固定的。假設(shè)申請的空間超過棧的剩余空間時,將提示overflow。
(2)堆:堆是高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)(它的生長方向與內(nèi)存的生長方向同樣)。是不連續(xù)的內(nèi)存區(qū)域。這是因為系統(tǒng)使用鏈表來存儲空暇內(nèi)存地址的。自然是不連續(xù)的,而鏈表的遍歷方向是由底地址向高地址。
堆的大小受限于計算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。
3.系統(tǒng)響應(yīng):
(1)棧:僅僅要棧的空間大于所申請空間,系統(tǒng)將為程序提供內(nèi)存,否則將報異常提示棧溢出。
(2)堆:首先應(yīng)該知道操作系統(tǒng)有一個記錄空暇內(nèi)存地址的鏈表,但系統(tǒng)收到程序的申請時,會遍歷該鏈表。尋找第一個空間大于所申請空間的堆結(jié)點(diǎn)。然后將該結(jié)點(diǎn)從空暇鏈表中刪除。并將該結(jié)點(diǎn)的空間分配給程序,另外,對于大多數(shù)系統(tǒng),會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小。這樣。代碼中的free語句才干正確的釋放本內(nèi)存空間。另外。找到的堆結(jié)點(diǎn)的大小不一定正好等于申請的大小,系統(tǒng)會自己主動的將多余的那部分又一次放入空暇鏈表中。
說明:對于堆來講,頻繁的malloc/free勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率減少。對于棧來講。則不會存在這個問題,
4.申請效率
(1)棧由系統(tǒng)自己主動分配,速度快。但程序猿是無法控制的
(2)堆是由malloc分配的內(nèi)存,一般速度比較慢,并且easy產(chǎn)生碎片。只是用起來最方便。
5.堆和棧中的存儲內(nèi)容
(1)棧:在函數(shù)調(diào)用時,第一個進(jìn)棧的主函數(shù)中后的下一條語句的地址。然后是函數(shù)的各個參數(shù),參數(shù)是從右往左入棧的,然后是函數(shù)中的局部變量。注:靜態(tài)變量是不入棧的。
當(dāng)本次函數(shù)調(diào)用結(jié)束后。局部變量先出棧。然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令,程序由該點(diǎn)繼續(xù)運(yùn)行。
(2)堆:通常是在堆的頭部用一個字節(jié)存放堆的大小。
6.存取效率
(1)堆:char *s1=”hellow?tigerjibo”;是在編譯是就確定的
(2)棧:char s1[]=”hellow?tigerjibo”;是在執(zhí)行時賦值的;用數(shù)組比用指針?biāo)俣雀煲恍?#xff0c;指針在底層匯編中須要用edx寄存器中轉(zhuǎn)一下,而數(shù)組在棧上讀取。
補(bǔ)充:
棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令運(yùn)行。這就決定了棧的效率比較高。
堆則是C/C++函數(shù)庫提供的,它的機(jī)制是非常復(fù)雜的,比如為了分配一塊內(nèi)存。庫函數(shù)會依照一定的算法(詳細(xì)的算法能夠參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,假設(shè)沒有足夠大小的空間(可能是因為內(nèi)存碎片太多)。就有可能調(diào)用系統(tǒng)功能去添加程序數(shù)據(jù)段的內(nèi)存空間。這樣就有機(jī)會分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然。堆的效率比棧要低得多。
7.分配方式:
(1)堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。
(2)棧有兩種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完畢的。比方局部變量的分配。
動態(tài)分配由alloca函數(shù)進(jìn)行分配,可是棧的動態(tài)分配和堆是不同的。
它的動態(tài)分配是由編譯器進(jìn)行釋放,無需手工實現(xiàn)。
四
參考1:http://blog.csdn.net/tigerjibo/article/details/7423728
參考2:http://blog.csdn.net/to_be_it_1/article/details/31420549
參考3:http://blog.csdn.net/lovecodeless/article/details/24384093
參考4:http://blog.csdn.net/lovecodeless/article/details/21084513
轉(zhuǎn)載于:https://www.cnblogs.com/yxwkf/p/5115601.html
總結(jié)
以上是生活随笔為你收集整理的【C语言天天练(二四)】内存分配的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第 五 课 golang语言变量
- 下一篇: springboot和quartz整合实