C++函數棧幀詳解

函數棧幀的創建和銷毀

在不同的編譯器下,函數調用過程中棧幀的創建是略有差異的,具體取決于編譯器的實現!

且需要注意的是,越高級的編譯器越不容易觀察到函數棧幀的內部的實現;

關于函數棧幀的維護這里我們要重點介紹兩個寄存器:ebp和esp

這兩個寄存器存放的是用來維護函數棧幀的地址!

每一個函數調用,都要在棧區創建一個空間!

問題:什么是函數棧幀?

函數棧幀實際上就是函數運行時棧上的一塊空間!用于存儲相對應的臨時數據!

接下來講解以x86系統為例:

這里對函數棧幀管理就是靠這兩個寄存器:

  • ebp:被稱為基址指針,指向棧幀的底部,(高地址處,且是固定位置!);
  • esp:被稱為棧頂指針,指向棧幀的頂部,(低地址處,地址可變);

這里需要注意的是,對于棧來說,是優先使用高地址的(棧的特性!)!?

問題:那么函數的棧幀中都存放了哪些數據?

從低地址到高地址依次存放了:

  • 調用棧幀的基址指針(ebp)--- 用于函數調用后恢復棧幀狀態;
  • 下一條指令的地址 --- 執行完該函數后跳轉到下一條指令;
  • 被調用的函數的參數(形參) --- 需要注意的是會從右到左進行壓棧;
  • 被調用函數的局部變量和臨時數據;
  • 寄存器的上下文(例如調用函數期間,使用的ebx、esi、edi等寄存器);

問題:main函數會被其他函數調用嗎??

需要注意的是,main函數也是可以被其他函數調用的:

即__tmainRTStartup會調用main函數!

問題:那么哪個誰調用?__tmainRTStartup這個函數?

__tmainRTStartup會被mainCRTStartup這個函數調用!

所以,這里我們總結一下:

因此,假如說當前我們的main函數里面調用了一個簡單的add函數,那么:

中間綠色的框是我們對應的main函數的調用堆棧,而在調用main函數之前,會先調用__tmainRTStartup和mainRTStartup這兩個函數!

而add函數在main函數上面,也就是對應的壓棧!

問題:但是函數棧幀中具體是怎么進行相關操作的?

示例代碼操作

這里我們以一個簡單的代碼為例,講解一下對應的相關操作:

其對應的匯編代碼如下所示:

需要注意的是,在調用main函數之前,調用main函數的那兩個函數的棧幀已經被創建好了!

這里我們對上面出現的匯編指令做一些簡單的解釋:

  • push實際上就是壓棧,將對應的數據壓入棧中;
  • mov:實際是就是賦值,這里mov ebp esp實際上就是把esp賦值給ebp;
  • sub:減去對應的地址;
  • lea(load effecitive address):計算內存地址并存入到寄存器當中(不訪問內存,僅計算結果);

這里實際上lea到rep stos這四行匯編代碼的作用就是將對應的棧的空間的數據都初始化為cc!

  • 壓棧:在棧頂上放一個元素;
  • 出棧:從棧頂刪除一個元素;

?截止到現在,做的都是初始化相關的任務,此時才開始到函數體內執行對應的任務;

假設每一行代表4個字節:

表示的就是將10這個值放到ebp-8的位置處;

?????????可以看到,也就是在ebp-8的位置上放10!

????????需要注意的是,上面這里我們是把10放進入了,如果沒有把10放進入,此時就是默認提供的隨機值,因此在C語言中,如果我們沒有進行初始化經常會打印出一堆燙燙燙燙(此時就是對應的內存棧上放的是一堆cccc)

此時可以看到對應的內存對其進行了修改(小端存儲)?

接下來我們再看int b = 20;這條匯編代碼:

dword ptr [ebp-14h], 14h

這段代碼實際上就是在ebp-14h這個地址處,填充數字20;

對應的示意圖如下所示:

?接下來我們再把int c = 0;也是在對應的棧上進行初始值:

當我們定義好對應的變量時,此時我們會調用add函數:

接下來我們按照對應的匯編代碼進行分析:

  • 這里eax指向[ebp -14h],也就是讓eax指向b;
  • 然后對eax進行壓棧;
  • 接下來讓ecx的值指向[ebp-8],也就是ecx指向a;
  • 然后在對ecx進行壓棧;

即截止到現在,我們進行的任務就是我們對應的傳參工作!

?接下來這里我們要調用對應的call指令:

call指令此時會跳轉地址,即這里會跳轉到我們對應的紅色線框對應的地址!

這里需要注意的是,call完成了兩個任務:

  • 將下一條指令的地址(00C21450)進行壓棧,壓入到棧中;
  • 跳轉到對應的地址執行函數體;

接下來就跳轉到對應的函數體當中:

其中,上面一堆的邏輯和main函數一樣,都是開辟對應的空間,然后進行初始化;

實際上代碼邏輯和我們上面講的是一樣的;

此時,我們依然假設每一行是4個字節,即此時每一行可以代表一個整形:

接下來我們依次看對應的匯編代碼:

int z = 0;
mov dword ptr [ebp-8,0]

?這里實際上就是把ebp-8指向的這個空間初始化為0;

然后這里把[ebp+8]的值賦值給eax當中:

這里[ebp+8]的值實際上就是之前我們的ecx的值也就是10!

然后再加上[ebp+och]的值,och換算為10進制為12,也就是這里我們之前eax的值!

加完之后,再把算出來的結果返回到ebp-8當中,也就是z!

問題:我們在函數棧幀中有創建對應的形參嗎?

沒有!在我們call進入函數體之前,我們就通過形參壓棧到對應的棧幀當中!

并且參數的壓棧順序是從右向左!

問題:如何再理解形參是實參的一份臨時拷貝呢?

這里我們在梳理下邏輯:

  • 在調用add函數之前,會對形參從右到左進行一份拷貝;
  • 而形參是調用函數之前,從main函數里面拷貝的實參!

可以看到上面的ecx和eax是對應的a和b的一份臨時拷貝!

所以改變形參不會改變對應的實參!

接下來我們再回到上面對應的代碼當中:上面我們只是把計算的值寫入到了z當中;

這里我們重點看return z的匯編代碼:

mov eax,dword ptr[ebp-8]

這里是把對應的返回值存入到了eax寄存器當中;

需要注意的是寄存器的值不會隨著函數棧幀被銷毀而丟失!

接下來執行pop對應的匯編代碼,這里也就是出棧,對應的esp棧頂指針會進行移動:

由于此時結果已經運行出來,保存到了eax寄存器當中,所以這里接下來直接對棧幀銷毀即可!

這里直接進行:

mov ebp,esp

此時棧頂指針直接指向棧底指針!

然后讓棧底指針進行出棧:

pop ebp

?實際上就是將add函數的棧底指針出棧,恢復到main函數當中;

pop:不僅對應的空間進行出棧,此時esp還需要+4個字節的地址;

ret

?ret指令實際上就是從棧頂跳出之前call的下一條指令的地址,然后跳轉過去;

需要注意的是:ebp和esp維護的是當前執行的函數的棧幀空間,而不是整個程序的棧幀空間!

接下來就返回到執行call之后的部分:

這里執行對esp執行add,實際上就是將對應的壓棧的形參進行銷毀;

此時上面壓棧的兩個形參也會被銷毀掉;?

然后將eax的值賦值給[ebp - 20h]這個位置!

那么這個ebp-20是什么呢?

實際上就是我們對應的參數c的值,這里把計算返回的值交到c當中!

講到這里,我們就實現了從計算值然后從函數棧幀返回出來的處理;

main函數的函數棧幀和add的大同小異,所以這里我們就不再過多介紹了!

所以接下來我們就可以回答一些問題了:

問題:為什么局部變量不初始化的時候是隨機值呢?

因為這里的局部變量是我們按要求設置的,例如vsstudio都初始化為cc;

問題:函數是如何進行傳參的?

實際上當我們還沒有調用函數的時候,此時形參就進行從右到左依次進行壓棧處理(臨時拷貝一份);

問題:函數調用的結果怎么返回?

值保存到寄存器當中,例如eax當中;且call會對下一條指令的地址進行壓棧,運行結束后再取出地址;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/78816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/78816.shtml
英文地址,請注明出處:http://en.pswp.cn/web/78816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CPU-GPU-NPU-TPU 概念

1.CPU 中央處理器(Central Processing Unit,簡稱CPU)作為計算機系統的運算和控制核心,是信息處理、程序運行的最終執行單元。CPU自產生以來,在邏輯結構、運行效率以及功能外延上取得了巨大發展。 2.GPU GPU&#xff0…

Java學習手冊:ORM 框架性能優化

一、優化實體類設計 減少實體類屬性 :僅保留必要的字段,避免持久化過多數據。例如,對于一個用戶實體類,如果某些信息(如詳細地址)不是經常使用,可以將其拆分到單獨的實體類中。使用合適的數據類…

XMP-Toolkit-SDK 編譯與示例程序

一、前言 最近在調研圖片的元數據讀寫方案,需要了解 XMP 空間以及如何在 XMP 空間中讀寫元數據,本文做一個相關內容的記錄。 XMP-Toolkit-SDK 以及 XMP標準簡介 XMP-Toolkit-SDK 是 Adobe 提供的一套開源軟件開發工具包(SDK)&a…

計算機硬件(南橋):主板芯片組FCH和PCH的區別

在計算機主板設計中,FCH(Fusion Controller Hub)和PCH(Platform Controller Hub)分別是AMD和Intel對主板芯片組中“南橋”(Southbridge)部分的命名。盡管兩者功能相似,但受不同廠商架…

數據庫系統概論-基礎理論

數據庫系統概述: 1、記錄:計算機中表示和存儲數據的一種格式或方法。 2、數據庫(DataBase, DB):數據庫是長期儲存在計算機內、有組織、可共享的大量數據集合。可為各種用戶共享。 3、數據庫管理系統(Dat…

在 R 中,清除包含 NA(缺失值)的數據

在 R 中,清除包含 NA(缺失值)的數據可以通過多種方式實現,具體取決于你希望如何處理這些缺失值。以下是幾種常見的方法,包括刪除包含 NA 的行、刪除包含 NA 的列,或者用特定值填充 NA。 1. 刪除包含 NA 的…

晶體布局布線

1Clock時鐘電路 時鐘電路就是類似像時鐘一樣準確運動的震蕩電路,任何工作都是依照時間順序,那么產生這個時間的電路就是時鐘電路,時鐘電路一般是由晶體振蕩器、晶振、控制芯片以及匹配電容組成 2.時鐘電路布局 晶體電路布局需要優先考慮&…

機器學習之嵌入(Embeddings):從理論到實踐

機器學習之嵌入(Embeddings):從理論到實踐 摘要 本文深入探討了機器學習中嵌入(Embeddings)的概念和應用。通過具體的實例和可視化展示,我們將了解嵌入如何將高維數據轉換為低維表示,以及這種轉換在推薦系統、自然語言處理等領域的實際應用…

Python初學者筆記第九期 -- (列表相關操作及列表編程練習題)

第17節課 列表相關操作 無論是內置函數、對象函數,用起來確實很方便,但是作為初學者,你必須懂得它們背后的運行邏輯! 1 常規操作 (1)遍歷 arr [1,2,3,4] # 以索引遍歷:可以在遍歷期間修改元素 for ind…

云計算與大數據進階 | 25、可擴展系統構建

在進入這個進階版系列之前,讓我們先回顧一下云計算與大數據系統的基本設計原則,總結起來有如下幾條: (1)基礎架構:更多采用商品現貨硬件(如PC架構)?,而很少使用定制化高端(如小型主…

C——函數遞歸

在 C 語言里&#xff0c;函數遞歸是一種函數調用自身的編程技術。下面開始逐一介紹。 一、什么是遞歸&#xff1f; 遞歸其實是?種解決問題的?法&#xff0c;在C語?中&#xff0c;遞歸就是函數??調???。 寫?個史上最簡單的C語?遞歸代碼&#xff1a; #include <st…

IdeaVim配置指南

一、什么是 IdeaVim&#xff1f; IdeaVim 是 JetBrains 系列 IDE&#xff08;如 IntelliJ IDEA, WebStorm, PyCharm 等&#xff09;中的一個插件&#xff0c;讓你在 IDE 里使用 Vim 的按鍵習慣&#xff0c;大大提升效率。 安裝方法&#xff1a; 在 IDE 中打開 設置(Settings) →…

Notepad++中XML格式化插件介紹

Notepad++中XML格式化插件介紹 背景安裝指南安裝步驟驗證安裝成功安裝失敗可嘗試使用說明XML文件格式正確時格式化錯誤格式檢查XML Tools插件核心功能盤點常見問題格式化后沒變化中文顯示亂碼拯救雜亂XML格式!Notepad++這個神器插件,必須接收!背景 接手別人寫的XML,縮進亂成…

自動化創業機器人:現狀、挑戰與Y Combinator的啟示

自動化創業機器人&#xff1a;現狀、挑戰與Y Combinator的啟示 前言 AI驅動的自動化創業機器人&#xff0c;正逐步從科幻走向現實。我們設想的未來是&#xff1a;商業分析、PRD、系統設計、代碼實現、測試、運營&#xff0c;全部可以在monorepo中由AI和人類Co-founder協作完成…

第1章 算法設計基礎

1-1 什么是算法 見識算法 算法是計算機科學的基石&#xff1a;從古代算術到現代計算機&#xff0c;算法始終是解決問題的核心。 算法的起源 張蒼《九章算術》&#xff1a;創立了機械化算法體系&#xff08;如“合分術”分數相加算法&#xff09;。 歐幾里得《幾何原本》&am…

java中ArrayList擴容機制的解析

本文將系統地介紹 Java 中 ArrayList 的擴容機制&#xff0c;包括其初始容量的設置、觸發擴容的時機、容量增長算法、擴容的詳細流程以及性能優化建議&#xff0c;幫助讀者從源碼層面深入理解這一關鍵特性&#xff0c;并在實際開發中合理預分配容量以提升性能。 一、ArrayList…

【網絡服務器】——回聲服務器(echo)

作用 實現回聲服務器的客戶端/服務器程序&#xff0c;客戶端通過網絡連接到服務器&#xff0c;并發送任意一串英文信息&#xff0c;服務器端接收信息后&#xff0c;執行數據處理函數&#xff1a;將每個字符轉換為大寫并回送給客戶端顯示。 客戶端&#xff1a;發送字符信息 服…

智能學習空間的范式革新:基于AI驅動的自習室系統架構與應用研究

摘要 在 “互聯網 + 教育” 深度融合的背景下,傳統自習室面臨個性化服務缺失、學習效率低下等瓶頸。本文提出一種基于人工智能技術的 AI 自習室系統架構,通過構建多模態數據感知、個性化學習引擎及智能環境調控模塊,實現學習過程的精準化、智能化與沉浸式體驗。研究結合計算…

HTML01:HTML基本結構

HTML基本結構 <html> <head><meta charset"UTF-8"><title>我的第一個網頁</title> </head> <body>我的第一個網頁 </body> </html><body、</body等成對的標簽&#xff0c;分別叫開發標簽和閉合標簽單獨…

Spring Boot 實現多種來源的 Zip 多層目錄打包下載(本地文件HTTP混合)

需要將一批文件&#xff08;可能分布在不同目錄、不同來源&#xff09;打包成Zip格式&#xff0c;按目錄結構導出給用戶下載。 1. 核心思路 支持將本地服務器上的文件&#xff08;如/data/upload/xxx.jpg&#xff09;打包進Zip&#xff0c;保持原有目錄結構。支持通過HTTP下載…