區域性變數(local variable)是指作用域和生命週期都侷限在所在函式或過程範圍內的變數,它是相對於全域性變數(global variable)而言的。編譯器在為區域性變數分配空間時通常有兩種做法:使用暫存器和使用棧。
暫存器的訪問速度快,但數量和空間有限,所以像字串或陣列不適合分配在暫存器中。編譯器通常只會把頻繁使用的臨時變數分配在暫存器中,比如for迴圈中的迴圈變數。當編譯器的優化選項開啟時,編譯器會充分利用可用的暫存器來給臨時變數使用,以提高程式的效能。對於除錯版本,優化選項預設是關閉的,編譯器會在棧上分配所有的變數。在c/c++程式中,可以在宣告變數時加上register關鍵字,請求編譯器在可能的情況下將該變數分配在暫存器中,但不能保證所描述的變數一定被分配在就存暫存器中。大多數時候,編譯器還是根據全域性設定和編譯器自身的邏輯來決定是否把乙個變數分配在暫存器中。
編譯器會在編譯階段根據變數的特徵和優化選項為每個區域性變數選擇以上的兩種分配方法之一。大多數的區域性變數都是分配在棧上的。棧上的變數會隨著函式的呼叫和返回而自動分配和釋放,所以棧有時也稱為自動記憶體。
區域性變數的分配和釋放是由編譯器插入的**通過調整棧指標(stack pointer)的位置來完成。編譯器在編譯時,會計算當前的**塊中所宣告的所有區域性變數所需要的空間,並將其按照記憶體對齊要求的最接近整數值。在32位系統中,記憶體分配時按4位元組對齊的,這意味著不滿4位元組的空間會按4位元組來分配。
計算好所需的空間後,編譯器會插入適當的指令來調整棧指標esp,為區域性變數分配空間,有兩種方式調整esp的值,一種是直接進行加減運算,另一種是push和pop指令。
當我們看到反彙編**時,常見到的指令是esp + n這種相對的位址,用esp進行標註的缺點是esp的值是不穩定的,當esp的值變化了,引用變數的偏移值也要變化。為了解決以上問題,x86 cpu設計了另乙個暫存器,這就是ebp暫存器。ebp的全稱是extended base pointer,即擴充套件的基位址指標。使用ebp暫存器,函式可以把自己將要使用的棧空間的基準位址記錄下來,然後使用這個基位址來引用區域性變數和引數。在同一函式內,ebp暫存器的值是保持不變的,這樣函式的區域性變數就有了乙個固定的參照物。
通常,乙個函式在入口處將當時的ebp值壓入堆疊,然後把esp值(棧頂)賦值給ebp,這樣ebp中的位址就是進入本函式時的棧頂位址。因此在ebp位址的上面便是這個函式使用的棧空間,它下面(位址值遞增方向)是父函式使用的空間例如ebp+4 指向的是call指令壓入函式的返回位址。ebp+8是父函式壓在棧上的第乙個引數,ebp+0xc是第二個引數。依次類推,ebp-n 是第乙個區域性變數的起始位址(n為變數的長度)。
因為在將棧頂位址(esp)賦給ebp暫存器之前先把舊的ebp值儲存在棧中,所以ebp暫存器所指向的棧單元中儲存的是前乙個ebp暫存器的值,這通常也就是父函式的ebp值。類似的父函式的ebp所指向的棧單元中儲存的是更上一層函式的ebp值,依此類推,直到當前執行緒的最頂層函式。這也正是棧回溯的基本原理。
棧中的資料是連續儲存的,好像所有的資料都混作一團,但事實上,它們是按照函式呼叫關係依此存放的,而且這種順序關係非常嚴格,為了更好地描述和指代棧中的資料,我們把每個函式在棧中所使用的區域稱為乙個棧幀(stack frame)。
關於棧幀還有以下幾點:
1. 在乙個棧中,依據函式呼叫關係,發起呼叫的函式(caller)的棧幀在下面(高位址方向),被呼叫的函式的棧幀在上面。
2. 每發生一次函式呼叫,便產生乙個新的棧幀,當乙個函式返回時,這個函式所對應的棧幀被清除(eliminated)
3. 執行緒正在執行的那個函式所對應的棧幀被位於棧的最頂部,它也是棧內仍然有效的最年輕的棧幀。
讀書筆記 區域性變數和棧幀
區域性變數 local variable 是指作用域和生命週期都侷限在所在函式或過程範圍內的變數,它是相對於全域性變數 global variable 而言的。編譯器在為區域性變數分配空間時通常有兩種做法 使用暫存器和使用棧。暫存器的訪問速度快,但數量和空間有限,所以像字串或陣列不適合分配在暫存器中...
全域性變數 區域性變數 棧 堆
一般全域性變數存放在資料區,區域性變數存放在棧區,動態變數存放在堆區,函式 放在 區。棧區是普通的棧資料結構,遵循lifo後進先出的規則,區域性變數安排在那裡是asm時就規定的,這樣可以在乙個函式結束後平衡堆疊,操作簡單,效率高 堆 動態區 在這裡應當叫堆疊 不要和資料結構中的堆搞混 是程式在編譯時...
Python筆記 區域性變數和全域性變數
a 100 這個變數是全域性變數,在整個py檔案裡都可以訪問 world hello deftest x hi 這個變數是在函式內部定義的變數,是區域性變數,只能在函式內部使用 print x format x 如果區域性變數的名和全域性變數同名,會在函式內部又定義乙個新的區域性變數,而不是修改全域...