專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

堆棧的深入個(gè)人理解

作者:wupingping   來源:wupingping   點(diǎn)擊數(shù):  更新時(shí)間:2014年06月21日   【字體:
對(duì)于,,堆棧的味道,一直就跟豬八戒吃啥來著,從沒有細(xì)細(xì)地品嘗過。發(fā)了下狠心,各個(gè)網(wǎng)頁看了很多,這里就把我東抓西拼的東西整理整理。
 
一  這些個(gè)概念怎么來的以及怎么記得住
這里,只限于整理我個(gè)人對(duì)堆/棧/堆棧在內(nèi)存管理方面的理解。其他,在數(shù)據(jù)結(jié)構(gòu)方面講的堆/棧目前不在我整理范圍之內(nèi)。
這里提了三個(gè)概念: 堆,棧,以及堆棧。我把堆棧的概念等同了。所以,接下來只要把兩個(gè)概念弄清楚就可以了:
 
先說由來。由于我的工作大部分是和單片機(jī)相關(guān)的,因此也是基于嵌入式的這個(gè)方面的理解。
每片MCU有一定的內(nèi)存,這些內(nèi)存分:程序存儲(chǔ)區(qū);數(shù)據(jù)存儲(chǔ)區(qū)。舉例就是:我們寫的每行代碼,都會(huì)保存到程序存儲(chǔ)區(qū)里面;而定義的一些全局變量,靜態(tài)變量,局部變量之類的,就保存到數(shù)據(jù)存儲(chǔ)區(qū)。
程序存儲(chǔ)區(qū),對(duì)于寫代碼的人來說,可以說是黑盒子:只要你的代碼不超過程序代碼空間,everything will be fine. 代碼究竟是如何執(zhí)行的,取決于你的編程思想,就是說,你讓程序往東走,它就會(huì)往東走;讓它原地踏步,它也會(huì)照做;前提是你的代碼要寫好。所以,對(duì)于編程經(jīng)驗(yàn)成熟的人,同樣的算法,他可能會(huì)少敲幾行代碼,并且算法出錯(cuò)的幾率少一些;不怎么會(huì)編程的人,就多敲幾行,多測(cè)試一下。研究深一些的人,可能還會(huì)對(duì)代碼執(zhí)行效率深入研究。我決定就此止步。繼續(xù)黑盒子的話題,程序員的代碼會(huì)燒寫到程序存儲(chǔ)區(qū)里面,至于哪條語句存哪里,這個(gè)是不用研究的。反正,一旦發(fā)現(xiàn)軟件沒按照你設(shè)計(jì)的那樣跑,基本就是程序自身的問題,不是存放程序的存儲(chǔ)區(qū)的問題。
數(shù)據(jù)存儲(chǔ)區(qū),讓程序員發(fā)揮的空間就很大了。由于程序是動(dòng)態(tài)地執(zhí)行的,執(zhí)行的結(jié)果是怎么樣,會(huì)產(chǎn)生什么數(shù)據(jù)就不是一個(gè)確定的事情。
舉個(gè)例子說說這個(gè)程序和數(shù)據(jù)之間的表象吧。舉例的事件就是:一個(gè)MP3播放器的上/下鍵的操作。加入在播放器里有10首歌,若當(dāng)前是第3首,那么按下[上]鍵后,應(yīng)該是播放第2首。因此,實(shí)現(xiàn)播放第2首的,就是程序員寫程序?qū)崿F(xiàn)的,他的代碼讓[上]鍵實(shí)現(xiàn)了能從第3首切換到第2首的操作。這里,表現(xiàn)的就是代碼。而且,這個(gè)代碼是確切的行為,從第3首按了[上]鍵之后,一定是播放到第2首。這個(gè)是確切的行為,如果不能實(shí)現(xiàn),就是代碼有bug了,需要改正。終于可以扯到數(shù)據(jù)了。第2首是什么歌呢? 就是說第2首歌的內(nèi)容是什么呢? 當(dāng)然在程序員寫代碼的時(shí)候,他是不知道的,他要做的,就是預(yù)先開一片數(shù)據(jù)空間,以存放歌曲相關(guān)的數(shù)據(jù)。這片數(shù)據(jù)空間,想放什么就放什么,是靈活的。聽煩了這幾首歌以后,再更換其它音頻文件就是了。再說具體一點(diǎn),程序就相當(dāng)于修的一條路,讓車可以在上面跑;但車上面裝了什么,程序員是不知道的。
 
女人就是啰嗦啊。女人的話題也是可以轉(zhuǎn)得很快的。
屬于數(shù)據(jù)存儲(chǔ)區(qū)的范疇,也可以算是數(shù)據(jù)管理的手段或方法。基于此,不能一概而論,說哪個(gè)手段高明一些;他們也是基于現(xiàn)實(shí)的需要而產(chǎn)生的。黑格爾說:存在就是合理的。
這樣的概念大家都不陌生:軍隊(duì)的管理是很嚴(yán)格的,很死板的;但是對(duì)于一些年輕的技術(shù)公司來說,員工就享有很高的自由度。
如果我說,墻角邊整齊地?cái)[放著10本書; 以及墻角邊的書凌亂地放著。你閉上眼睛,能區(qū)分出兩種畫面嗎? 如果沒有,就不用往下看了。
棧的特性,就是嚴(yán)格/有序/規(guī)范的。棧的英文就是Stack. 如果我說,there are books stacked in that corner,你應(yīng)該能知道書是怎么放的吧。
相反,堆呢,就是自由/靈活/隨意的。堆的英文是Heap. 如果我說,there are books heaped in that corner,你應(yīng)該能知道書是怎么放的吧。 
 
二  get closer to the real STACK/HEAP。
: 由系統(tǒng)自動(dòng)分配和回收的。
: 由程序員分配和回收的。
基于第一部分的理解,不用想都知道,作為“堆”的數(shù)據(jù)空間,也必須是靈活的,因?yàn)槌汕先f的程序員在寫什么程序是未知的。但可知道的一點(diǎn),就是他們是跑在確定的某個(gè)OS里面的。
因此,也不過就是給系統(tǒng)管理的數(shù)據(jù)空間起了個(gè)名字,就;給程序員使用的空間,起了個(gè)名,就
我接下來就會(huì)廢話:起什么名字都不重要,重要的是,我們得對(duì)這兩種數(shù)據(jù)存儲(chǔ)區(qū)的管理的機(jī)制由來,方法有深刻認(rèn)識(shí);這樣,即便幾個(gè)世紀(jì)以后它們更名為阿貓阿狗了,我們依然能認(rèn)知它們。
 
舉例: 
void Check_Pro_Code( uint8  style )
{
     uint8 i;
 
     switch( style )
     {
      ......
     }
}
 
void main( )
{
     uint8 j = 1;
     
     Check_Pro_Code( j );              
}
 
在main()函數(shù)里調(diào)用了Check_Pro_Code(...)函數(shù),事先要對(duì)j進(jìn)行入棧操作;當(dāng)然這里函數(shù)調(diào)用的時(shí)候,涉及到幾個(gè)入棧操作:程序的下一個(gè)執(zhí)行地址;局部變量;形參。
這里,我就沒有深入介紹了。
 
實(shí)在很慚愧的是,我寫的嵌入式軟件里,沒有涉及到任何和操作相關(guān)的。我就是那樣一個(gè)人,CM3內(nèi)核里也沒有移植操作系統(tǒng),實(shí)在是汗顏,因?yàn)楸救藢?duì)RTOS實(shí)在是未曾涉獵。所以,我這里對(duì)于堆的介紹,是沒有任何實(shí)戰(zhàn)鷹眼的。并且為了我就了一下。你說,這樣算學(xué)術(shù)造假嗎?
void main( )
{
int j=10;
int *p;
 
p = "123456".
}

三  話說堆棧溢出
再次明確,堆棧溢出堆棧是指。
1  當(dāng)C程序函數(shù)的調(diào)用層次過深或者出現(xiàn)了遞歸調(diào)用,就容易使程序運(yùn)行所需的堆�?臻g超過系統(tǒng)能提供的最大堆�?臻g范圍,產(chǎn)生堆棧溢出。
這是很明白的,當(dāng)A函數(shù)調(diào)用了B函數(shù),而B()里面又調(diào)用了C(),C()里又調(diào)用了D()......當(dāng)這樣的調(diào)用太深的時(shí)候,就容易堆棧溢出了。  
 
四   一級(jí)緩存/二級(jí)緩存
棧使用的是一級(jí)緩存, 他們通常都是被調(diào)用時(shí)處于存儲(chǔ)空間中,調(diào)用完畢立即釋放。
堆則是存放在二級(jí)緩存中,生命周期由虛擬機(jī)的垃圾回收算法來決定(并不是一旦成為孤兒對(duì)象就能被回收)。所以調(diào)用這些對(duì)象的速度要相對(duì)來得低一些。
棧的優(yōu)勢(shì)是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。
舉個(gè)我知道的例子: 
void main( )
{
int i,j;
static int flag = 1;
 
i = Sum_Of_Group( );     
j = Check_Exist( );
}
這里,i,j的值都是給通用寄存器的,而不是給予確切的物理地址。而對(duì)于flag,由于其為靜態(tài)變量,是在SRAM里面分配地址的。這里好像沒有說到一級(jí)緩存二級(jí)緩存。時(shí)間限制,下回述。
 
五  :在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。
:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。
注:該段內(nèi)容100%抄襲,出處:網(wǎng)絡(luò)。 
 
六  如何修改我自己的工程的STACK(以下內(nèi)容不通用)
由于我自己的工程用的是LPC1765,剛好從網(wǎng)絡(luò)抄到了比較有用的圖片。摘于此。其他單片機(jī)的編譯環(huán)境,應(yīng)該也是差不多如法炮制的。
編譯環(huán)境: IAR for ARM.
圖一  :如何修改STACK的大小
 
       
 圖二,如何知道自己的工程用了多少堆棧
 

 
 
 
整理了2個(gè)小時(shí),僅以此文,紀(jì)念我美好生活的另一種開始。
 
 
 
 
 
關(guān)閉窗口

相關(guān)文章