欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言超詳細講解函數(shù)棧幀的創(chuàng)建和銷毀

 更新時間:2022年05月05日 15:37:29   作者:野豬佩奇`  
我們知道c語言中函數(shù)都是被調(diào)用的,main函數(shù)里面能調(diào)用其他函數(shù),其實main函數(shù)也是被別的函數(shù)調(diào)用的,下面通過本文給大家分享c語言函數(shù)棧幀的創(chuàng)建和銷毀過程,一起看看吧

1、本節(jié)目標

C語言絕命七連問,你能回答出幾個?

  • 局部變量是如何創(chuàng)建的?
  • 為什么局部變量不初始化其內(nèi)容是隨機的?
  • 有些時候屏幕上輸出的"燙燙燙"是怎么來的?
  • 函數(shù)調(diào)用時參數(shù)時如何傳遞的?傳參的順序是怎樣的?
  • 函數(shù)的形參和實參的關(guān)系是什么?
  • 函數(shù)的返回值是如何帶回的?
  • 函數(shù)是怎樣在棧區(qū)上開辟和釋放空間的?

想要對上面的這六個問題做出準確深入的回答,我們需要學(xué)習(xí)函數(shù)棧幀的創(chuàng)建和銷毀相關(guān)知識,在正式進入函數(shù)棧幀之前,我們還需要了解一些相關(guān)的寄存器和匯編指令。

2、相關(guān)寄存器

  • eax:通用寄存器,保留臨時數(shù)據(jù),常用于返回值。
  • ebx:通用寄存器,保留臨時數(shù)據(jù)。
  • ebp:棧底寄存器,用來記錄棧底的地址。
  • esp:棧頂寄存器,用來記錄棧頂?shù)牡刂贰?/li>
  • eip:指令寄存器,保存當前指令的下一條指令的地址。

3、相關(guān)匯編指令

  • mov:數(shù)據(jù)轉(zhuǎn)移指令。
  • sub:減法命令。
  • add:加法命令。
  • push:數(shù)據(jù)入棧,同時esp棧頂寄存器也要發(fā)生改變。
  • pop:數(shù)據(jù)彈出至指定位置,同時esp棧頂寄存器也要發(fā)生改變。
  • call:函數(shù)調(diào)用,1. 壓入返回地址 2. 轉(zhuǎn)入目標函數(shù)。
  • jump:通過修改eip,轉(zhuǎn)入目標函數(shù),進行調(diào)用。
  • lea:傳遞地址指令,用于加載有效地址。
  • ret:恢復(fù)返回地址,壓入eip,類似pop eip命令。

4、什么是函數(shù)棧幀

函數(shù)棧幀(stack frame)就是函數(shù)調(diào)用過程中在程序的調(diào)用棧(call stack)所開辟的空間,這些空間是用來存放:

  • 函數(shù)參數(shù)和函數(shù)返回值。
  • 臨時變量(包括函數(shù)的非靜態(tài)的局部變量以及編譯器自動生產(chǎn)的其他臨時變量)。
  • 保存上下文信息(包括在函數(shù)調(diào)用前后需要保持不變的寄存器)。

同時,每一次函數(shù)調(diào)用,編譯器都會為該函數(shù)分配一塊空間,而這塊空間就被稱為這個函數(shù)的函數(shù)棧幀;并且,這塊空間是由兩個寄存器來維護的:esp寄存器(記錄棧頂?shù)牡刂罚┖蚭bp寄存器(記錄棧底的地址)。

5、什么是調(diào)用堆棧

函數(shù)調(diào)用堆棧是反饋函數(shù)調(diào)用邏輯的。我們以main函數(shù)的調(diào)用為例:

我們可以看到,mainCRTStartup調(diào)用__scrt_common_main,__scrt_common_main調(diào)用__scrt_common_main_seh,__scrt_common_main_seh調(diào)用_SCRT_STARTUP_MAIN,_SCRT_STARTUP_MAINmain,main調(diào)用Add。

6、函數(shù)棧幀的創(chuàng)建和銷毀

我們以一段程序為例講解函數(shù)棧幀:(注意: 函數(shù)棧幀的創(chuàng)建和銷毀過程,在不同的編譯器上實現(xiàn)的方法和細節(jié)會有所差異,一般來說,越新的編譯器對函數(shù)棧幀的封裝就越嚴密,本次演示以VS2019為例。)

演示代碼

#include<stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 3;
	int b = 5;
	int ret = 0;
	ret = Add(a, b);
	printf("%d", ret);
	return 0;
}

(1)、main函數(shù)棧幀的創(chuàng)建與初始化

(2)、main函數(shù)的核心代碼

(3)、Add函數(shù)的調(diào)用過程

F11進入Add函數(shù)內(nèi)部,觀察Add函數(shù)的反匯編代碼

代碼執(zhí)行到Add函數(shù)的時候,就要開始創(chuàng)建Add函數(shù)的棧幀空間了。

在Add函數(shù)中創(chuàng)建棧幀的方法和在main函數(shù)中是相似的,在棧幀空間的大小上略有差異而已。

1. 將main函數(shù)的 ebp 壓棧。

2. 計算新的 ebp 和 esp。

3. 將 ebx , esi , edi 寄存器的值保存。

4. 計算求和,在計算求和的時候,我們是通過 ebp 中的地址進行偏移訪問 到了函數(shù)調(diào)用前壓棧進去的參數(shù),這就是形參訪問。

5. 將求出的和放在 eax 寄存器中準備帶回。

(4)、Add函數(shù)棧幀的銷毀

當函數(shù)調(diào)用要結(jié)束返回的時候,前面創(chuàng)建的函數(shù)棧幀也開始銷毀,具體銷毀過程如下:

(5)、調(diào)用完成

調(diào)用完Add函數(shù),回到main函數(shù)的時候,繼續(xù)往下執(zhí)行,可以看到:

00BE185D add esp,8 esp直接+8,相當于跳過了main函數(shù)中壓棧的a’和b’。

00BE1860 mov dword ptr [ebp-20h],eax 將eax中值,存檔到ebp-0x20的地址處,其實就是存儲到main函數(shù)中ret變量中,而此時eax中就是Add函數(shù)中計算的x和y的和,可以看出來,本次函數(shù)的返回值是由eax寄存器帶回來的。程序是在函數(shù)調(diào)用返回之后,在eax中去讀取返回值的。

7、對開篇問題的解答

當我們完整的了解了函數(shù)棧幀創(chuàng)建和銷毀的過程后,我們就可以回答開篇提到的問題了:

1.局部變量是如何創(chuàng)建的?

局部變量的創(chuàng)建是在局部變量所在的函數(shù)的棧幀創(chuàng)建完成并初始化后,然后在該棧幀內(nèi)為局部變量分配空間的。

2.為什么局部變量不初始化其內(nèi)容是隨機的?

因為編譯器在創(chuàng)建函數(shù)棧幀后會在棧幀空間里面放入一個值,而這個值是隨機的。

3.有些時候屏幕上輸出的"燙燙燙"是怎么來的?

因為main函數(shù)調(diào)用時,在棧區(qū)開辟的空間的其中每一個字節(jié)都被初始化為0xCC,而如果我們定義的是一個未初始化的數(shù)組,且這個數(shù)組恰好在這塊空間上創(chuàng)建,因為0xCCCC(兩個連續(xù)排列的0xCC)的漢字編碼是“燙”,所以屏幕上輸出的就是燙燙燙。

4.函數(shù)調(diào)用時參數(shù)時如何傳遞的?傳參的順序是怎樣的?

我們在調(diào)用函數(shù)之前,就會在棧頂上從右向左依次壓入需要傳遞的參數(shù),在創(chuàng)建好被調(diào)函數(shù)的函數(shù)棧幀后通過指針的偏移量來使用傳遞過去的參數(shù),而不是在被調(diào)函數(shù)的函數(shù)棧幀內(nèi)創(chuàng)建形參。

5.函數(shù)的形參和實參的關(guān)系是什么?

形參是實參的一份臨時拷貝,二者的存儲位置不同,形參的改變不會影響實參。

6.函數(shù)的返回值是如何帶回的?

函數(shù)的返回值通過eax寄存器帶回。

7.函數(shù)是怎樣在棧區(qū)上開辟和釋放空間的?

函數(shù)通過改變esp和edp的指向來創(chuàng)建和銷毀空間,空間銷毀并不會清除該空間中的數(shù)據(jù),下一次使用該空間時新數(shù)據(jù)直接覆蓋原數(shù)據(jù)即可。

到此這篇關(guān)于C語言超詳細講解函數(shù)棧幀的創(chuàng)建和銷毀的文章就介紹到這了,更多相關(guān)C語言函數(shù)棧幀內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++內(nèi)存管理面經(jīng)

    C++內(nèi)存管理面經(jīng)

    這篇文章主要介紹了C++的內(nèi)存分配方式以及介紹了下棧和堆的區(qū)別,感興趣的小伙伴可以參考閱讀本文
    2023-03-03
  • OpenCV?通過Mat遍歷圖像的方法匯總

    OpenCV?通過Mat遍歷圖像的方法匯總

    對圖像中的所有點或特殊點進行運算,所以遍歷圖像就顯得很重要,如何高效的遍歷圖像是一個很值得探討的問題,本文給大家?guī)砹硕喾N方法操作OpenCV?通過Mat遍歷圖像,感興趣的朋友一起看看吧
    2022-02-02
  • C語言實現(xiàn)簡單的三子棋游戲源碼

    C語言實現(xiàn)簡單的三子棋游戲源碼

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡單的三子棋游戲源碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • VC++文件監(jiān)控之ReadDirectoryChangesW

    VC++文件監(jiān)控之ReadDirectoryChangesW

    文章主要介紹文件監(jiān)控的另一種實現(xiàn)方式,利用ReadDirectoryChangesW來實現(xiàn)文件的監(jiān)控,希望對大家有幫助
    2019-04-04
  • 詳解如何利用C++實現(xiàn)Mystring類

    詳解如何利用C++實現(xiàn)Mystring類

    這篇文章主要為大家詳細介紹了C++實現(xiàn)MyString的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 有關(guān)C++繼承與友元、繼承與類型轉(zhuǎn)換詳解

    有關(guān)C++繼承與友元、繼承與類型轉(zhuǎn)換詳解

    下面小編就為大家?guī)硪黄嘘P(guān)C++繼承與友元、繼承與類型轉(zhuǎn)換詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • QT實戰(zhàn)之打開最近圖片功能的實現(xiàn)

    QT實戰(zhàn)之打開最近圖片功能的實現(xiàn)

    這篇文章主要為大家詳細介紹了如何利用Qt和QSettings實現(xiàn)打開最近圖片功能,文中的示例代碼講解詳細,對我們學(xué)習(xí)QT有一定的幫助,感興趣的可以了解一下
    2022-06-06
  • 在C++中自定義宏的簡單方法

    在C++中自定義宏的簡單方法

    這篇文章主要介紹了在C++中自定義宏的簡單方法,作者建議使用類似定義函數(shù)一樣的方法來定義宏,需要的朋友可以參考下
    2015-07-07
  • Qt自定義控件實現(xiàn)圓圈加載進度條

    Qt自定義控件實現(xiàn)圓圈加載進度條

    這篇文章主要為大家詳細介紹了Qt自定義控件實現(xiàn)圓圈加載進度條,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • C++的虛析構(gòu)詳解及實例代碼

    C++的虛析構(gòu)詳解及實例代碼

    這篇文章主要介紹了C++的虛析構(gòu)詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評論