原文:
一、位址空間與物理記憶體
(1)位址空間與物理記憶體是兩個完全不同的概念,真正的**及資料都存在物理記憶體中。
物理儲存器是指實際存在的具體儲存器晶元,cpu在操縱物理儲存器的時候都把他們當做記憶體來對待,把他們看成由若干個儲存單元組成的邏輯儲存器,這個邏輯儲存器就是我們所說的位址空間。
位址空間大小與邏輯儲存器大小不一定相等。
(2)程序的位址空間分布
程序的位址空間包括:棧區(heap)、共享區、堆區(stack)、未初始化靜態全域性區、已初始化靜態全域性區、靜態唯讀區、**段。如圖:
二、棧幀的建立
首先要明白幾個地方:
每乙個函式都有自己的棧幀空間,並且獨佔自己的棧幀空間,
當前正在執行的函式的棧幀總是在棧頂。win32系統提供兩個特殊的暫存器用於標識位於系統棧頂端的棧幀。
這裡main是呼叫者(caller);fun是被呼叫者(callee)在函式呼叫前,main正在用esp和ebp暫存器指示它自己的棧幀。hljs axapta has-numbering" style="display:block; padding:0px; font-family:consolas,inconsolata,courier,monospace; line-height:22px; overflow-x:auto; word-wrap:normal">int fun(int a,int b)
int main()
main把eax,ecx和edx壓棧。這是乙個可選的步驟,只在這三個暫存器內容需要保留的時候執行此步驟。
接著,main把傳遞給fun的引數一一進棧,最後的引數最先進棧,這裡也就解釋了函式在壓棧過程中是從右往左,即先壓4,再壓3。
(1)這裡首先main函式建立自己的棧幀結構;main()函式是由—__tcrtstartup()函式呼叫的,所以maincrtstratup()函式呼叫__tmaincrtstra()函式的時候就會從棧上為__tmaincrtstra()分配類似圖中這麼一塊空間,因為我們現在要呼叫main()函式了,所以當然要先把__tmaincrtstartup()函式的執行狀態儲存下來,這樣main()函式才能返回的時候才能找得到!。
然後繼續執行下一條語句: mov ebp,esp
即把esp的值賦給ebp,這樣,ebp也就指向了現在esp的位置
然後sub esp 0c0h 這樣就為main函式開闢了一段空間然後將ebx、esi、edi暫存器壓棧就形成如圖所示:
緊接著將區域性變數及實參壓棧,並執行call指令,main用call指令呼叫子函式:
call fun
當call指令執行的時候,eip指令指標暫存器的內容被壓入棧中。因為eip暫存器是指向main中的下一條指令,所以現在返回位址就在棧頂了。在call指令執行完之後,下乙個執行週期將從名為fun的標記處開始.
當函式fun,也就是被呼叫者取得程式的控制權,它必須做3件事:建立它自己的棧幀,為區域性變數分配空間,最後,如果需要,儲存暫存器ebx,esi和edi的值。
首先fun必須建立它自己的棧幀。ebp暫存器現在正指向main的棧幀中的某個位置,這個值必須被保留,因此,ebp進棧。然後esp的內容賦值給了 ebp。這使得函式的引數可以通過對ebp附加乙個偏移量得到,而棧暫存器esp便可以空出來做其他事情。如此一來,幾乎所有的c函式都由如下兩個指令開 始:
push ebp
mov ebp, esp
下一步,fun必須為它的區域性變數分配空間,同時,也必須為它可能用到的一些臨時變數分配 空間。比如,foo中的一些c語句可能包括複雜的表示式,其子表示式的中間值就必須得有地方存放。這些存放中間值的地方同城被稱為臨時的,因為他們可以為 下乙個複雜表示式所復用
現在,區域性變數和臨時儲存都可以通過基準指標ebp加偏移量找到了。
最後,如果fun用到ebx,esi和edi暫存器,則它必須在棧裡儲存它們。
fun的函式體現在可以執行了。這其中也許有進棧、出棧的動作,棧指標esp也會上下移動,但ebp是保持不變的。這意味著我們可以一直用[ebp+…]找到第乙個引數,而不管在函式中有多少進出棧的動作。
函式fun的執行也許還會呼叫別的函式,甚至遞迴地呼叫foo本身。然而,只要ebp暫存器在這些子呼叫返回時被恢復,就可以繼續用ebp加上偏移量的方式訪問實際引數,區域性變數和臨時儲存。
緊接著當被呼叫者執行完畢時將消除棧幀結構,呼叫pop指令。
在把程式控制權返還給呼叫者前,被呼叫者foo必須先把返回值儲存在eax暫存器中。其次,foo必須恢復ebx,esi和edi暫存器的值。進棧和出棧操作的次數必須保持平衡。
在程式控制權返回到呼叫者main)後,這時,傳遞給fun的引數通常已經不需要了。我們可以把引數一起彈出棧,這可以通過把棧指標實現:
add esp, 8
此時fun函式呼叫結束棧幀結構恢復至圖一。
如果在函式呼叫前,eax,ecx和edx暫存器的值被儲存在棧中,呼叫者main函式現在可以把它們彈出。這個動作之後,棧頂就回到了我們開始整個函式呼叫過程前的位置。
這樣整個函式的呼叫就結束了
C語言 函式呼叫過程(棧幀)
首先舉個栗子 include int add int x,int y int main 在這個程式裡,函式被呼叫才會發揮函式的功能,而函式的呼叫其實是乙個過程,在這個過程計算機要為函式開闢棧空間,用於本次函式臨時變數的儲存和現場保護,這塊空間稱為函式的棧幀。現場保護的作用是為了在呼叫完另乙個函式,返...
C語言函式呼叫過程 棧幀
在學習過函式宣告和定義,了解了函式的引數 實參 形參 引數的設計 函式的使用等一些函式基礎知識之後,函式逐漸變為我們編寫 時重要工具。無論是編寫時引用的庫函式,還是實現程式部分功能時使用的自定義函式,都體現函式的重要性。函式特點 使 開發更高效 提高 復用性 使 邏輯更加清晰。函式所佔據的重要地位,...
C語言 深度理解函式的呼叫(棧幀)
本文講解的函式呼叫棧幀,需要用到彙編。又不知道同學可以自學一下。我們可以知道函式的呼叫是乙個過程,我們通常將這個過程稱之為函式的呼叫過程。從邏輯上講,棧幀就是乙個函式執行的環境 函式引數 函式的區域性變數 函式執行完後返回到 等等。這個過程要為函式開闢棧空間,於本次函式的呼叫中臨時變數 的儲存 現場...