在講解堆棧之前,我們先要來(lái)說(shuō)說(shuō)其實(shí)我們常說(shuō)的堆棧是兩種數(shù)據(jù)結(jié)構(gòu),。那么什么是堆什么又是棧呢? 棧,,是硬件。主要作用表現(xiàn)為一種數(shù)據(jù)結(jié)構(gòu),是只能在某一端插入和刪除的特殊線性表,。它按照后進(jìn)先出的原則存儲(chǔ)數(shù)據(jù),先進(jìn)入的數(shù)據(jù)被壓入棧底,,最后的數(shù)據(jù)在棧頂,,需要讀數(shù)據(jù)的時(shí)候從棧頂開(kāi)始彈出數(shù)據(jù)(最后一個(gè)數(shù)據(jù)被第一個(gè)讀出來(lái))。棧是允許在同一端進(jìn)行插入和刪除操作的特殊線性表,。允許進(jìn)行插入和刪除操作的一端稱為棧頂(top),,另一端為棧底(bottom);棧底固定,,而棧頂浮動(dòng),;棧中元素個(gè)數(shù)為零時(shí)稱為空棧。插入一般稱為進(jìn)棧(PUSH),,刪除則稱為退棧(POP),。 棧也稱為先進(jìn)后出表。??梢杂脕?lái)在函數(shù)調(diào)用的時(shí)候存儲(chǔ)斷點(diǎn),,做遞歸時(shí)要用到棧! 以上定義是在經(jīng)典計(jì)算機(jī)科學(xué)中的解釋,。 在計(jì)算機(jī)系統(tǒng)中,,棧則是一個(gè)具有以上屬性的動(dòng)態(tài)內(nèi)存區(qū)域。程序可以將數(shù)據(jù)壓入棧中,,也可以將數(shù)據(jù)從棧頂彈出,。在i386機(jī)器中,棧頂由稱為esp的寄存器進(jìn)行定位,。壓棧的操作使得棧頂?shù)牡刂窚p小,,彈出的操作使得棧頂?shù)牡刂吩龃蟆? 棧在程序的運(yùn)行中有著舉足輕重的作用。最重要的是棧保存了一個(gè)函數(shù)調(diào)用時(shí)所需要的維護(hù)信息,,這常常稱之為堆棧幀或者活動(dòng)記錄,。堆棧幀一般包含如下幾方面的信息: 1. 函數(shù)的返回地址和參數(shù)2. 臨時(shí)變量:包括函數(shù)的非靜態(tài)局部變量以及編譯器自動(dòng)生成的其他臨時(shí)變量。 堆,,是一種動(dòng)態(tài)存儲(chǔ)結(jié)構(gòu),,實(shí)際上就是數(shù)據(jù)段中的自由存儲(chǔ)區(qū),它是C語(yǔ)言中使用的一種名稱,,常常用于動(dòng)態(tài)數(shù)據(jù)的存儲(chǔ)分配,。堆中存入一數(shù)據(jù),總是以2字節(jié)的整數(shù)倍進(jìn)行分配,,地址向增加方向變動(dòng),。堆可以不斷進(jìn)行分配直到?jīng)]有堆空間為止,,也可以隨時(shí)進(jìn)行釋放、再分配,,不存在次序問(wèn)題,。 明白了堆和棧的概念之后我們來(lái)看一個(gè)面試的c語(yǔ)言題目。代碼要相關(guān)要求如下所示: #include <iostream> } print(); 剛剛一開(kāi)始看到這個(gè)題目時(shí),,你可能有點(diǎn)發(fā)懵,心想可能在不傳遞參數(shù)的情況下打印arr數(shù)組的內(nèi)容,,但是看看我們的標(biāo)題就應(yīng)該知道該題跟棧有關(guān)系,,在做之前我們先來(lái)回顧幾個(gè)知識(shí)點(diǎn)。 push操作先修改指針,,后將信息入棧,。 ESP為堆棧指針,棧頂有ESP寄存器來(lái)定位,。壓棧的操作使得棧頂?shù)牡刂窚p小,,彈出的操作使得棧頂?shù)牡刂吩龃蟆?/p> EBP是32位的BP,EBP是基址指針,,EBP與BP的關(guān)系就像AX與AL,、AH的關(guān)系一樣。BP為基指針寄存器,,用它課直接存取堆棧中的數(shù)據(jù),,他的作用在調(diào)用函數(shù)時(shí)保存ESP,,使函數(shù)結(jié)束時(shí)可以正確返回。 c的默認(rèn)函數(shù)壓棧操作為: 我們先來(lái)看看上面這段代碼的匯編語(yǔ)句: //*******************************************start*********************************************// .file "push.c" //*******************************************end*********************************************// 看看上面的匯編代碼,,我們的重點(diǎn)放在紅色字體部分,,在函數(shù)的開(kāi)頭部分都有這么兩行代碼: pushl %ebp --------------------------->保存上一個(gè)函數(shù)的棧底 明白了上面的內(nèi)容,,那么我們就可以實(shí)現(xiàn)題目的要求了。代碼如下所示: #include <iostream> unsigned int _ebp; } print(); 其中用紅色標(biāo)記的部分是一個(gè)重點(diǎn),,用匯編語(yǔ)句mov _ebp,ebp;來(lái)獲得ebp寄存器的值,,存放在_ebp中,,ebp存放當(dāng)前函數(shù)棧底的地址,就是說(shuō)ebp可以看做一個(gè)指針,,指向棧頂,,因?yàn)?span style="COLOR: #ff0000"> pushl %ebp,所以棧頂存放的數(shù)據(jù)就是上一個(gè)函數(shù)的ebp的值,,即就是main函數(shù)的棧底,。由此可知*(int*)_ebp意思就是取出棧頂?shù)臄?shù)據(jù),即main函數(shù)的棧底,。棧中數(shù)據(jù)的存儲(chǔ)方式是根據(jù)聲明的先后順序來(lái)的,,所以了為了能夠?qū)rr數(shù)組進(jìn)行打印,我們要計(jì)算出數(shù)組首地址的存儲(chǔ)地址,,由于變量的壓棧方式默認(rèn)是四字節(jié)對(duì)齊,,所以我們使用一句int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);來(lái)得到數(shù)組的首地址,有人可能很疑惑了,,為什么這句 char *str="fdsafdsafdsafdsafdsafdsafdsa";聲明的變量只是占了四字節(jié)呢,?!這里要注意了,,因?yàn)橹羔樧兞康牡刂吩?2位的計(jì)算機(jī)中占用四個(gè)字節(jié),,他的內(nèi)容并不存儲(chǔ)在棧中,而是在堆中,棧中僅僅是保存了指向堆的指針,。講到這兒基本上都豁然開(kāi)朗了吧,。看看下面的運(yùn)行結(jié)果吧,!
代碼如下,,紅色部分為修改代碼,。 #include <iostream> unsigned int _ebp; } char srt[6]; print(); 運(yùn)行結(jié)果為: 如果我們修改了 char srt[6];之后去把int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);修改為int *p=(int *)(*(int *)_ebp-4-4-4-4-6-7*4);,,注意紅色部分的對(duì)比,,運(yùn)行結(jié)果就變?yōu)榱耍?/span> 顯然對(duì)比可知運(yùn)行結(jié)果出錯(cuò)了。在此多次一舉的給出對(duì)比無(wú)非是為了大家能夠?qū)ψ止?jié)的對(duì)齊方式加以重視,。當(dāng)然以上內(nèi)容難免有錯(cuò),,畢竟c語(yǔ)言博大精深,如果有不正確的地方,,請(qǐng)糾正,。 如需轉(zhuǎn)載,請(qǐng)注明出處,! 如果你也喜歡C語(yǔ)言,,跟我一樣對(duì)于C語(yǔ)言想知其然更想知其所以然,,那么歡迎你來(lái)我的博客空間閱讀我的其他C語(yǔ)言文章,。我的博客地址:http://blog.csdn.net/bigloomy |
|