1. 程式人生 > >C 程式區域性變數壓棧出棧的理解

C 程式區域性變數壓棧出棧的理解

        寫這篇總結的緣由僅僅出於巧合,五一前幫一位同學看51的程式,在檢視彙編程式碼的時候(事實上我當時的彙編知識基本都還給了老師),無意中問起我“某個區域性變數的宣告怎麼沒有對應的彙編語句”,我沒有答出來。當時也只是把它當做一種常識給記了下來,平時不論還是在DSP、16位的單片上還是PC平臺上編寫c程式,由於程式不是很複雜且晶片資源通常足夠,因此很少會考慮記憶體分配、堆疊方面的內容。這幾天在看《Linux c 一站式程式設計》這本書關於變數內容的時候突然想起這個問題,頓時覺得很有必要把深究一下。

        因為網路賬號還沒充錢的緣故,ubuntu系統還不能及時升級,因此在VC平臺下做了以下相關試驗。程式碼如下:

int main(void)

{

         int d;

         int f = d+1;

         d = 1;

         d = hello(5,10);

        return 0;

}

        對應的彙編程式碼如下:

16:   int main(void)

17:   {

00410960   push        ebp

00410961   mov         ebp,esp

00410963   sub         esp,48h

00410966   push        ebx

00410967   push        esi

00410968   push        edi

00410969   lea         edi,[ebp-48h]

0041096C   mov         ecx,12h

00410971   mov         eax,0CCCCCCCCh

00410976   rep stos    dword ptr [edi]

18:

19:

20:       int d;

21:       int f = d+1;

00410978   mov         eax,dword ptr [ebp-4]

0041097B   add         eax,1

0041097E   mov         dword ptr [ebp-8],eax

22:       d = 1;

00410981   mov         dword ptr [ebp-4],1

23:        //printf('Hello world!\n');

24:        //printf("a = %f,b=%f,c=%f",a,b,c);

25:        //getch();

26:       d = hello(5,10);

00410988   push        0Ah

0041098A   push        5

0041098C   call        @ILT+5(_hello) (0040100a)

00410991   add         esp,8

00410994   mov         dword ptr [ebp-4],eax

27:       return 0;

00410997   xor         eax,eax

28:   }

        從以上彙編程式碼20、21兩行中可以看到“int d”語句確實沒有對應的函式宣告。但是在用d給f賦值的時候,d卻對應了dword ptr [ebp-4],這一點剛開始的時候很是疑惑,後來在看過幾篇相關文章後得知,區域性變數的宣告和釋放是由編譯器調整棧指標(Stack Pointer)位置來完成的[1],編譯器首先根據變數的型別和數量計算儲存區域性變數所需的空間,然後調整ESP的值來為區域性變數分配空間。“int d;” 在編譯的時候應該是將變數d 與地址 dword prt[ebp-4]對應了起來,既d的記憶體地址為ebp-4到ebp-7;同理,f值往上調整,對應ebp-8到ebp-11。

        然而,瞭解了以上內容還遠不夠理解函式壓棧出棧的原理,於是對main函式下面對應的17行做了分析,考慮linux平臺下的情況[],主要針對ESP和EBP兩個棧暫存器作分析解釋。以下是通過單步除錯的方式得到的結果:

圖1  未單步前

       未執行單步執行前,程式和暫存器值如圖1所示。ESP = 0012FF84 ,EBP = 0012FFC0;這是舊的(在函式呼叫時就是主調函式的)棧對應的空間。

圖2  執行  “00410960   push        ebp”

           執行的“push ebp”後,程式和暫存器值如圖2所示。ESP = 0012FF80,EBP = 0012FFC0;因為是要在原來棧的基礎上開闢新的棧空間,因此,在舊棧(不知道該如何稱呼,暫且這麼認為)的棧頂上移了四個位元組作為新棧的棧底。因此ESP由原來的0012FF84變為0012FF80 。

圖3  執行  “00410961   mov         ebp,esp”

       “mov  ebp,esp”是為了將新的棧底指標賦值為ebp,如圖3所示。ESP = 0012FF80,EBP = 0012FF80 。

圖4  執行  “00410963   sub         esp,48h”

       “ sub   esp,48h”則是為了給區域性變數在棧中分配一定的儲存空間,如圖4所示。ESP由原來的0012FF80減小到0012FF38,這裡需要注意的是0012FF80到0012FF7C為舊棧中的棧頂對應的四個位元組,因此區域性變數的分佈實際是從地址0012FF7C到地址0012FF34共72個位元組空間。

圖5  執行  “push        ebx ;push        esi;push        edi”

        接下來,連續執行三個push,將相應的暫存器值壓棧。

        時間限制,下面轉寫了參考中的文獻以備忘。

1在一個棧中,依據函式呼叫關係,發起呼叫的函式(caller)的棧幀在下面(高地址方向),被呼叫的函式的棧幀在上面。

2每發生一次函式呼叫,便產生一個新的棧幀,當一個函式返回時,這個函式所對應的棧幀被清除(eliminated

”[1]

相關推薦

C 程式區域性變數理解

        寫這篇總結的緣由僅僅出於巧合,五一前幫一位同學看51的程式,在檢視彙編程式碼的時候(事實上我當時的彙編知識基本都還給了老師),無意中問起我“某個區域性變數的宣告怎麼沒有對應的彙編語句”,我沒有答出來。當時也只是把它當做一種常識給記了下來,平時不論還是在DSP

遍歷C語言程式

#include<stdio.h> #include<malloc.h> #include<stdlib.h> typedef struct Node { int data; struct Node * pNext; }NODE,*

C語言區域性變數在記憶體中的順序

首先總結規則,詳細分析見下面: 規則1:記憶體由低到高優先分配給佔位8位元組、4位元組、2位元組、1位元組的資料型別 資料型別佔位說明: 8位元組:double、longlong int 4

C++ 簡單實現

/********************************************************************** * Copyright (c)2015,WK S

微信小程式同一個頁面入歷史兩次,返回第二次頁面沒資料

微信小程式同一個頁面壓入歷史棧兩次,返回第二次頁面沒資料 問題描述 最近專案中遇到一個場景,頁面A(A1) => B => A(A2), 然後返回時,先從頁面返回時,從A2 => B => A1, A2 回退時,會觸發 A2 的 onUnload(

C++】判斷順序順序的合法性

//判斷順序棧出棧順序的合法性思想: //給定一個入棧的字串pushstr,再給定一個出棧的字串popstr,根據棧的特性,判斷該出棧順序是否合法 //1.先驗證兩個字串的長度是否相等,不等,則肯定不合法;相等,再進行步驟2. //2.(1)判斷pushstr與popstr

2014年第五屆藍橋杯C/C++程式設計本科B組決賽 次序(結果填空)

2014年第五屆藍橋杯C/C++程式設計本科B組決賽題目彙總: 出棧次序 X星球特別講究秩序,所有道路都是單行線。一個甲殼蟲車隊,共16輛車,按照編號先後發車,夾在其它車流中,緩緩前行。 路邊有

java入門---資料結構操作例項之的方法實現字串反轉

    以下例項演示了使用使用者自定義的方法 StringReverserThroughStack() 來實現字串反轉:import java.io.IOException; public class

關於區域性變數的指標,,和的釋放的幾個例子

看幾個例子 一個是經典的1+2=65534: 程式碼如下 #include <stdio.h> #include <stdlib.h> unsigned short *Sum( unsigned char a,unsigned char b)

順序的基本操作 初始化 進 頂元素

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

c語言區域性變數應該注意什麼?

去年我們部門在17屆大學生在轉正述職答辯時,有位同學(同事)被領導提出一個問題:“c語言區域性變數應該注意什麼?” 當時我的第一反應就是,static靜態變數,命名與全域性變數避免衝突。我記得那位同學說的是注意記憶體,今天突然想到這個問題,我就把知道的都寫出來了(歡迎大家在下面進行修正和補充

1143.字母轉換(進

1143.字母轉換 時限:1000ms 記憶體限制:10000K  總時限:3000ms 描述 通過棧交換字母順序。給定兩個字串,要求所有的進棧和出棧序列(i表示進棧,o表示出棧),使得字串2在求得的進出棧序列的操作下,變成字串1。輸出結果需滿足字典序。例如TROT 到

連結串列與——陣列入、單鏈表翻轉

陣列入棧出棧 class Stack{ private Object[] data = new Object[0]; //棧的內容 private int size = 0; //棧的元素個數

C語言--區域性變數作為指標返回值的問題

程式碼: #include <stdio.h> #include <stdlib.h> int* getStu(int x, int y) {         int* i;         *i = x+y;         return i; }

Anagrams by Stack(進問題)

Anagrams by Stack How can anagrams result from sequences of stack operations? There are two sequences of stack operators which can conver

的入頂(連結串列實現)

基於連結串列實現入棧,出棧,取棧頂元素的操作 鏈式棧 入棧 (連結串列頭插) 比較方便 出棧 (連結串列頭刪) 標頭檔案

C++程式語言變數命名規範

1、  整型字首   int               nId;           //int字首:n short             sId;            //short字首:s unsigned int      unId          // 

深入理解計算機系統-之-數值儲存(二)--C程式列印變數的每一位元組或者位

大端與小端 前面我們提到了依據CPU端模式的不同,資料的儲存順序也不一樣。 採用大小模式對資料進行存放的主要區別在於在存放的位元組順序,BE big-endian 大端模式 ,最直觀的位元組序 ,地址低位儲存值的高位,地址高位儲存值的低位 ,不需要考慮對

連結串列 進遍歷清空

#include <stdio.h> #include <stdlib.h> #include <malloc.h> // 定義一個節點的結構 typedef struct node { int member;

的問題(彙總)

#include <stdio.h> #include <malloc.h> typedef   struct{  int   *stk;   int   top;    int   size; }stack; void initstack(stack   *s,   i