关于栈的理解(读书笔记)
生活随笔
收集整理的這篇文章主要介紹了
关于栈的理解(读书笔记)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
關于棧的理解(讀書筆記)
標簽: 棧內存布局可執行程序 2013-03-16 02:16 2957人閱讀 評論(1) 收藏 舉報 分類: 【C語言學習】(56)版權聲明:本文為博主原創文章,未經博主允許不得轉載。
目錄(?)[+]
? ? 關于對棧的理解,相信很多人和我一樣都是很模糊的。在昨天閱讀數據的時候,看到對這方面的介紹,便寫個這個博客來和大家分享下。希望對大家有所幫助。
? ? ?棧,是硬件,主要作用表現為一種數據結構,是只能在一端插入和刪除數據的特殊線性表。允許進行插入和刪除操作的一端稱為棧頂,另一端為棧底。棧按照后進先出的原則存儲數據,最先進入的數據被壓入棧底,最后進入的數據在棧頂,需要讀數據時從棧頂開始彈出數據。棧底固定,而棧頂浮動。棧中元素個數為零時稱為空棧。插入一般稱為進棧(p u s h ),刪除則稱為出棧(p o p )。 ?棧也被稱為先進后出表,在函數調用的時候用于存儲斷點,在遞歸時也要用到棧。
? ? ?在計算機系統中,棧則是一個具有以上屬性的動態內存區域。程序可以將數據壓入棧中,也可以將數據從棧頂彈出。在i 3 8 6 機器中,棧頂由稱為e s p 的寄存器進行定位。壓棧的操作使棧頂的地址減小,彈出的操作使棧頂的地址增大。?棧在程序的運行中有著舉足輕重的作用。最重要的是,棧保存了一個函數調用時所需要
的維護信息,這常常被稱為堆棧幀。棧一般包含以下兩方面的信息:?
1 )函數的返回地址和參數。
2 )臨時變量:包括函數的非靜態局部變量及編譯器自動生成的其他臨時變量。?
? ? ? ?堆,是一種動態存儲結構,實際上就是數據段中的自由存儲區,它是C 語言中使用的一種名稱,常常用于存儲、分配動態數據。堆中存入的數據地址向增加方向變動。堆可以不斷進行分配直到沒有堆空間為止,也可以隨時進行釋放、再分配,不存在順序問題。堆內存的分配常通過m a l l o c ( ) 、c a l l o c ( ) 、r e a l l o c ( ) 三個函數來實現。而堆內存的釋放則使用f r e e ( ) 函數。
? ? ?堆和棧在使用時“生長”方向相反,棧向低地址方向“生長”,而堆向高地址方向“生長”。
? ? [csharp] view plaincopyprint?
1 )p u s h 操作先移動棧頂指針,之后將信息入棧。
2 )e s p 為堆棧指針,棧頂由e s p 寄存器來定位。壓棧的操作使棧頂的地址減小,彈出的操作使棧頂的地址增大。
3 )e b p 是3 2 位的b p ,是基址指針。b p 為基指針寄存器,用它可直接存取堆棧中的數據,它在調用函數時保存e s p ,以便函數結束時可以正確返回。
4 )默認的函數內部變量的壓棧操作為:從上到下、從左向右,采用4 字節對齊。數組壓棧方法略有不同,即從最后一個元素開始,直到起始元素為止,即采用從右向左的方法壓棧。
?函數的開頭都有如下兩句匯編指令:
push ? ? ? ?ebp
mov ? ? ? ? ebp,esp
為了使讀者易于理解,在此通過圖1 - 1 來分析說明。根據圖上的標注,函數開頭部分的第一個p u s h 指令的操作步驟是,首先移動棧頂指針e s p ,然后將e b p 內容壓棧,注意此時壓棧的e b p 的值為上一個函數的e s p 的值, ?而e s p 恰好就是上一個函數的棧底,所以每個函數一開始的p u s h 指令就是保存上一個函數的棧底。那么接下來的m o v 指令有什么作用呢?由于e s p 是當前的棧頂指針,所以該指令的作用就是保存當前棧頂指針的值。由此就可以分析出,e b p 存放的是此刻棧頂的地址,就是說,e b p 是一個指針,指向棧頂,而棧頂存放的數據其實是上一個函數的e b p 的值,即上一個函數的棧底。
? ? 通過上面的分析可知,e b p 壓棧后,接著就是函數中臨時變量的壓棧操作,由此可知,我們只需要在p r i n t ( ) 函數中得到m a i n ( ) 函數的棧底,就可以取出數組中的每個元素了,看看下面的實現方法。
[csharp] view plaincopyprint? ? ? ?在沒有傳遞任何參數的情況下,成功地在p r i n t ( ) 函數中打印出了m a i n ( ) 函數中a r r 數組內的每個元素。現在來看看上面代碼的實現方法,在p r i n t ( ) 函數中定義了一個_ e b p 無符號整型變量,通過V C + + ? 6 . 0 內嵌匯編把e b p 的值保持到_ e b p 中,按照上面的分析,可以將在函數p r i n t ( ) 中通過這條內嵌匯編語句得到的e b p 看成一個指針,指針所指向的單元存放的就是p r i n t ( ) 函數的上一個函數的棧底,在此是m a i n ( ) 函數的棧底。知道了_ e b p 的作用后,我們來分析下代碼,通過( i n t * ) _ e b p 將_ e b p 轉換為一個整型指針,然后通過* ( i n t * ) _ e b p 即可得到m a i n ( ) 函數的棧底地址。由于棧的壓棧操作是從上到下、從右到左的,所以m a i n ( ) 函數中的變量a 先壓棧,然后是b 、c ,最后是a r r 數組,數組的壓棧順序是從右到左。通過“i n t ?* p = ( i n t ? * ) ( * ( i n t ? * ) _ e b p - 4 - 4 - 4 - 7 * 4 ) ; ”即可得到數組元素的首地址。接下來,根據首地址就可以取出數組中的每個元素了。有的讀者可能會有一個疑惑,m a i n ( ) 函數中有一個字符型變量,是不是在求數組元素的首地址時應該把其中的減4 改為減1 呢?因為它只占用了一個字節!即將“i n t ? * p = ( i n t ? * ) ( * ( i n t ? * ) _ e b p - 4 - 4 - 4 - 7 * 4 ) ; ”修改為“i n t ? * p = ( i n t ? * ) ( * ( i n t ? * ) _ e b p - 4 - 4 - 1 -7 * 4 ) ”。我們暫且不說其對與錯,先來看看修改后的運行結果:3072 ? ?3328 ? ?3584 ? ?3840 ? ?4096 ? ?4352 ? ?-859021056 ? ? ?
? ? ?我們發現這樣的運行結果是錯誤的,為什么呢?細心的讀者可能發現了本章一開始回顧的知識點中有一點是很重要的,那就是壓棧操作為4 字節對齊。所以這里必須減4 ,而不是減1?
-------------------------------------------------------------以上是書本原話介紹---------------------------------------
? ? 閱讀完上面的代碼應該會對棧有一定的了解。下面簡單介紹下可執行程序在內存中的布局!
? ? ?
? ? ?數據段包含經過初始化的全局和靜態變量及他們的數值。BSS段的大小從可執行程序中得到,然后又連接器得到這個大小的內存塊,緊跟在數據段的后面。當這個內存區進行程序的地址空間后全部清零。包括數據段和BSS段的整個區域統稱為數據區。值得說明的是局部變量并未編譯到可執行程序中,是在程序運行時執行的。
總結
以上是生活随笔為你收集整理的关于栈的理解(读书笔记)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python求输入数字的平方、如果平方运
- 下一篇: java ftp详解_Java实现FTP