中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C語言函數棧幀如何創建和銷毀

發布時間:2022-03-04 09:53:42 來源:億速云 閱讀:174 作者:小新 欄目:開發技術

這篇文章主要為大家展示了“C語言函數棧幀如何創建和銷毀”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“C語言函數棧幀如何創建和銷毀”這篇文章吧。

    寫在前面

    我們知道,每一次函數調用都需要在棧區上為其開辟一塊空間,這塊空間就叫做這個函數的棧幀。

    而棧是從高地址向低地址延伸的。每個函數的每次調用,都有它自己獨立的一個棧幀,這個棧幀中維持著所需要的各種信息。寄存器ebp指向當前的棧幀的底部(高地址),寄存器esp指向當前的棧幀的頂部(低地址)。

    這樣我們就了解了寄存器ebp和寄存器esp中存放的是地址,這兩個地址是用來維護函數棧幀的。比如:調用main函數, 我們為main函數分配棧幀空間, 那么棧幀維護如下:

    C語言函數棧幀如何創建和銷毀

    下面我們通過一段代碼分析一下,函數棧幀創建和銷毀的過程:(棧幀這部分內容在不同的編譯器上實現存在差異, 但是思想大致都是一致的。本文是在vs2013編譯器下實現的。)

    #include <stdio.h>
    int Add(int x, int y)
    {
    	int z = 0;
    	z = x + y;
    	return z;
    }
    int main(void)
    {
    	int a = 10;
    	int b = 20;
    	int ret = 0;
    	ret = Add(a, b);//計算a+b
    	printf("%d\n", ret);
    	return 0;
    }

    我們在調試過程打開調用堆棧

    C語言函數棧幀如何創建和銷毀

    可以看出,main函數是在__tmainCRTStartup函數內部被調用的,而__tmainCRTStartup函數又是在mainCRTStartup函數內部調用的。

    為了能更加清楚的看到棧幀創建和銷毀的過程,我們轉到上面代碼對應的反匯編代碼:

    int main(void)
    {
    009D3F40  push        ebp  //將edp壓入棧幀
    009D3F41  mov         ebp,esp  //將esp的值賦給edp
    009D3F43  sub         esp,0E4h  //esp-0E4h
    009D3F49  push        ebx  
    009D3F4A  push        esi  
    009D3F4B  push        edi  
    009D3F4C  lea         edi,[ebp+FFFFFF1Ch]  
    009D3F52  mov         ecx,39h  
    009D3F57  mov         eax,0CCCCCCCCh  
    009D3F5C  rep stos    dword ptr es:[edi]  
    	int a = 10;
    009D3F5E  mov         dword ptr [ebp-8],0Ah  
    	int b = 20;
    009D3F65  mov         dword ptr [ebp-14h],14h  
    	int ret = 0;
    009D3F6C  mov         dword ptr [ebp-20h],0  
    	ret = Add(a, b);//計算a+b
    009D3F73  mov         eax,dword ptr [ebp-14h]  
    009D3F76  push        eax  
    009D3F77  mov         ecx,dword ptr [ebp-8]  
    009D3F7A  push        ecx  
    009D3F7B  call        009D11F9  
    009D3F80  add         esp,8  
    009D3F83  mov         dword ptr [ebp-20h],eax  
    	printf("%d\n", ret);
    009D3F86  mov         esi,esp  
    009D3F88  mov         eax,dword ptr [ebp-20h]  
    009D3F8B  push        eax  
    009D3F8C  push        9D5860h  
    009D3F91  call        dword ptr ds:[009D9118h]  
    009D3F97  add         esp,8  
    009D3F9A  cmp         esi,esp  
    009D3F9C  call        009D1140  
    	return 0;
    009D3FA1  xor         eax,eax  
    }
    009D3FA3  pop         edi  
    009D3FA4  pop         esi  
    009D3FA5  pop         ebx  
    009D3FA6  add         esp,0E4h  
    009D3FAC  cmp         ebp,esp  
    009D3FAE  call        009D1140  
    009D3FB3  mov         esp,ebp  
    009D3FB5  pop         ebp  
    009D3FB6  ret

    main函數的調用 main函數棧幀的創建

    經過剛才我們的理解,在準備調用main函數的時候,調用main函數的那個函數的棧幀已經開辟好了。

    C語言函數棧幀如何創建和銷毀

    然后將ebp壓入棧幀,保存了指向棧底的ebp的地址,而此時esp指向新的棧頂位置;接著將esp的值賦給了ebp,產生了新的ebp;用esp減去一個16進制數0E4H(這里就是為main函數預開辟空間)。緊接著三個壓棧指令,分別將ebx,esi,edi,壓入棧幀。加載完有效地址以后,將為main函數預開辟空間全部初始化為0xCCCCCCCC。最后創建了三個局部變量a,b,ret并進行了初始化。

    Add函數的調用

    函數傳參

    C語言函數棧幀如何創建和銷毀

    將b的值存入寄存器eax中,再將eax壓入棧中;將a的值存入寄存器ecx中,再將將ecx壓入棧中;這里看出參數是從右向左傳遞的。緊接著執行call指令,這里就是調用Add函數,同時將call指令的下一條指令的地址壓入棧中,然后執行call指令的時候按F11 , 就進入了Add函數內部。

    Add函數棧幀的創建

    C語言函數棧幀如何創建和銷毀

    首先將main()函數的ebp壓入棧,保存指向main()函數棧幀底部的ebp的地址,此時esp指向新的棧頂位置;將esp的值賦給ebp,產生新的ebp,即Add()函數棧幀的ebp;給esp減去一個16進制數0E4H,這里是為Add()函數預開辟空間;緊接著三個壓棧指令,分別將ebx,esi,edi,壓入棧幀。加載完有效地址以后,將為Add函數預開辟空間全部初始化0xCCCCCCCC。在緊接著創建了變量z,將形參的a和b相加的結果存儲到z中;最后將結果存儲到eax寄存器中,通過寄存器帶回了函數的返回值。

    Add函數棧幀的銷毀

    C語言函數棧幀如何創建和銷毀

    edi、esi、ebx依次出棧,esp 會向下移動;然后將ebp的值賦給esp,使esp指向ebp指向的地方;接著ebp 出棧,同時將出棧的內容給ebp,此時ebp又指向了main函數棧幀的底部,最后執行ret 指令,表示出棧一次,并跳轉到出棧的內容的地址處,也就是call指令的下一條指令處。

    main函數棧幀的銷毀

    C語言函數棧幀如何創建和銷毀

    main函數棧幀的銷毀和Add函數棧幀銷毀的過程的思想都是一樣的,這里就不做多贅述了。

    以上是“C語言函數棧幀如何創建和銷毀”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    盖州市| 邵阳县| 罗江县| 长寿区| 弋阳县| 桓仁| 松江区| 新津县| 徐水县| 濮阳市| 亚东县| 海兴县| 确山县| 那坡县| 台中市| 繁峙县| 进贤县| 南通市| 东丰县| 苏尼特右旗| 兴和县| 河曲县| 合水县| 聂拉木县| 荃湾区| 延寿县| 广元市| 涞源县| 巴中市| 新密市| 深水埗区| 文昌市| 曲周县| 嵩明县| 远安县| 积石山| 永新县| 吐鲁番市| 武夷山市| 米林县| 新营市|