【進階】--函數棧幀的創建和銷毀詳解

目錄

一.函數棧幀的概念

二.理解函數棧幀能讓我們解決什么問題

三.相關寄存器和匯編指令知識點補充

四.函數棧幀的創建和銷毀

4.1.調用堆棧

4.2.函數棧幀的創建

4.3 函數棧幀的銷毀



?一.函數棧幀的概念

--在C語言中,函數棧幀是指在函數調用過程中,在內存棧中為該函數分配的一塊空間,用于存儲函數的局部變量,參數,返回地址等信息。

棧幀的結構:

  • 參數區:用于存放調用函數時傳遞給被調用函數的參數。
  • 返回地址:記錄函數調用結束后要返回的指令地址,以便函數執行完畢后能正確回到調用點繼續執行。
  • 局部變量區:存儲函數內部定義的局部變量。
  • ebp和esp相關區域:ebp指向當前棧幀的底部,esp指向當前棧幀的頂部,通過這兩個指針來維護函數棧幀。

二.理解函數棧幀能讓我們解決什么問題

--在前期的學習中,我們可能會產生很多困惑

比如:

  • 局部變量是怎么創建的
  • 為什么局部變量的值是隨機值
  • 函數是怎么傳參的?傳參的順序是怎樣的?
  • 形參和實參是什么關系?
  • 函數調用是怎么做的?
  • 函數調用結束后是怎么返回的?

當我們理解函數棧幀的創建和銷毀后,我們就可以更好的去解決這些問題,如同修練自己的內功,也方便在后期能搞懂更多的知識。


三.相關寄存器和匯編指令知識點補充

相關寄存器:

  • eax:通用寄存器,保留臨時數據,常用于函數返回值
  • ebx:通用寄存器,保留臨時數據
  • eip:指令寄存器,用于存儲下一條要執行的指令的地址
  • ebp:棧底寄存器
  • esp:棧頂寄存器

相關匯編指令:

  • push:將操作數壓入棧中,棧頂指針esp也會相應調整
  • pop:從棧中彈出數據到指定的位置,棧頂指針esp也會相應調整
  • mov:數據傳送指令,用于在寄存器之間,寄存器與內存之間傳送數據
  • add:加法指令,用于將兩個操作數相加,結果存放于指定的寄存器中
  • sub:減法指令,用于將兩個操作數相減,結果存放于指定的寄存器中
  • call:過程調用,壓入返回地址或轉入調用函數
  • lea:加載有效地址指令,將操作數的地址加載到指定的寄存器中
  • ret:返回地址指令,回到調用位置

四.函數棧幀的創建和銷毀

4.1.調用堆棧

代碼如下:

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

這段代碼我們在vs2022上調試的話,調試進入add函數后,我們就可以觀察到函數的調用堆棧(右擊勾選,顯示外部代碼)?,如下圖所示

函數調用堆棧是可以反饋函數調用邏輯的,我們可以清晰的觀察到,是由invoke_main函數來調用main函數的?,在此之間的我們就不過多的去考慮了,我們接下來直接從main函數的棧幀創建開始。

4.2.函數棧幀的創建

--當函數每次被調用時,系統都會在棧上為該函數分配一塊棧幀空間。首先將調用函數的相關信息,如參數,返回地址等壓入棧中,然后調整ebp和esp,為局部變量分配空間

我們先將main函數轉到反匯編--調試到main函數第一行時,右鍵鼠標轉到反匯編,反匯編代碼如下

int main()
{
//函數棧幀的創建
005518D0  push        ebp  
005518D1  mov         ebp,esp  
005518D3  sub         esp,0E4h  
005518D9  push        ebx  
005518DA  push        esi  
005518DB  push        edi  
005518DC  lea         edi,[ebp-24h]  
005518DF  mov         ecx,9  
005518E4  mov         eax,0CCCCCCCCh  
005518E9  rep stos    dword ptr es:[edi]  
005518EB  mov         ecx,55C008h  
005518F0  call        0055132F  
005518F5  nop  
//main函數中的主要代碼int a = 10;
005518F6  mov         dword ptr [ebp-8],0Ah  int b = 20;
005518FD  mov         dword ptr [ebp-14h],14h  int c = 0;
00551904  mov         dword ptr [ebp-20h],0  c = Add(a, b);
0055190B  mov         eax,dword ptr [ebp-14h]  
0055190E  push        eax  
0055190F  mov         ecx,dword ptr [ebp-8]  
00551912  push        ecx  
00551913  call        005510B9  
00551918  add         esp,8  
0055191B  mov         dword ptr [ebp-20h],eax
------------------------------------------------------------  printf("%d\n", c);
0055191E  mov         eax,dword ptr [ebp-20h]  
00551921  push        eax  
00551922  push        557B30h  
00551927  call        005510D7  
0055192C  add         esp,8  return 0;
0055192F  xor         eax,eax  
}

?我們可以將上面main函數的函數棧幀創建過程的主要部分單獨拆解出來看看,代碼如下

005518D0  push        ebp  
//把ebp寄存器中的值進行壓棧,到了esp-4的位置,此時的ebp中存放的是invoke_main函數棧幀的ebp
005518D1  mov         ebp,esp  
//將esp的值存放到ebp中,相當于ebp來到了invoke_main函數棧幀的esp位置,產生了main函數的ebp
005518D3  sub         esp,0E4h  
//將esp中的地址減去一個16進制數字0E4h,esp向上移動,產生了新的esp,也就是main函數的esp
//結合上面產生的ebp之后,ebp和esp之間就維護了一塊為main函數開辟的棧幀空間
005518D9  push        ebx  
//將寄存器ebx中的值壓棧,esp-4,esp向上移動
005518DA  push        esi 
//將寄存器epi中的值壓棧,esp-4,esp繼續向上移動
005518DB  push        edi 
//將寄存器edi中的值壓棧,esp-4,esp接著向上移動
005518DC  lea         edi,[ebp-24h]  
005518DF  mov         ecx,9  
005518E4  mov         eax,0CCCCCCCCh  
005518E9  rep stos    dword ptr es:[edi]  
//以上這四串代碼是在初始化main函數的棧幀空間
//1.先將ebp-24h的地址加載到edi中
//2.將9放入ecx中
//3.將0xCCCCCCCC放入eax中
//4.將從ebp-24h到ebp之間ecx個4個字節的數字初始化為0xCCCCCCCC

?接下來再來分析main函數中的主要代碼

	int a = 10;
005518F6  mov         dword ptr [ebp-8],0Ah 
//將0Ah存儲到ebp-8這個地址中,ebp-8的位置其實就是變量aint b = 20;
005518FD  mov         dword ptr [ebp-14h],14h 
//將14h存儲到ebp-14h這個地址中,ebp-14h的位置其實就是變量bint c = 0;
00551904  mov         dword ptr [ebp-20h],0  
//將0存儲到ebp-20h這個地址中,ebp-20h的位置其實就是變量c
以上就是局部變量在其所在函數的棧幀空間中創建和初始化的過程c = Add(a, b);
0055190B  mov         eax,dword ptr [ebp-14h] 
// 先傳參b,將ebp-14h位置中b的值存儲到eax中
0055190E  push        eax  
//將eax的值壓棧,esp-4,向上移動
0055190F  mov         ecx,dword ptr [ebp-8]  
//再傳參a,將ebp-8位置中a的值存儲到ecx中
00551912  push        ecx 
//將ecx的值壓棧,esp-4,繼續向上移動 //跳轉調用函數
00551913  call        005510B9  
//call指令會將call指令的下一條指令的地址進行壓棧操作
//這樣做可以讓函數調用結束后回到call的下一條指令地址后繼續執行
00551918  add         esp,8  
0055191B  mov         dword ptr [ebp-20h],eax  

call指令會執行函數調用邏輯,這個時候我們會跳轉到Add函數中,我們再來觀察Add函數的反匯編代碼

int Add(int x, int y)
{
00551790  push        ebp 
00551791  mov         ebp,esp 
00551793  sub         esp,0CCh  
00551799  push        ebx  
0055179A  push        esi  
0055179B  push        edi  
0055179C  lea         edi,[ebp-0Ch]  
0055179F  mov         ecx,3  
005517A4  mov         eax,0CCCCCCCCh  
005517A9  rep stos    dword ptr es:[edi]  
005517AB  mov         ecx,55C008h  
005517B0  call        0055132F  
005517B5  nop  int z = 0;
005517B6  mov         dword ptr [ebp-8],0  z = x + y;
005517BD  mov         eax,dword ptr [ebp+8]  
005517C0  add         eax,dword ptr [ebp+0Ch]  
005517C3  mov         dword ptr [ebp-8],eax  return z;
005517C6  mov         eax,dword ptr [ebp-8]  
}
005517C9  pop         edi  
005517CA  pop         esi  
005517CB  pop         ebx  
005517CC  add         esp,0CCh  
005517D2  cmp         ebp,esp  
005517D4  call        00551253  
005517D9  mov         esp,ebp  
005517DB  pop         ebp  
005517DC  ret 

代碼執行到Add函數的時候就要開始創建Add函數的棧幀空間了,與前面main函數的棧幀空間創建過程差不多,這里就不詳細講述了,主要是計算求和的時候我們通過偏移訪問,訪問到了函數調用前壓棧進去的參數,這就是形參訪問,很好說明了形參就是實參的一份臨時拷貝,最后將求出的和通過eax寄存器中帶回。

	z = x + y;
005517BD  mov         eax,dword ptr [ebp+8] 
// 將ebp+8地址處的數字存儲到eax中
005517C0  add         eax,dword ptr [ebp+0Ch] 
// 將ebp+12地址處的數字加到eax寄存中
005517C3  mov         dword ptr [ebp-8],eax  
//將eax的結果保存到ebp-8的地址處,其實就是放到z中return z;
005517C6  mov         eax,dword ptr [ebp-8] 
//將ebp-8地址處的值放在eax中,其實就是把z的值存儲到eax寄存器中,通過eax寄存器帶回計算的結果

4.3 函數棧幀的銷毀

--函數執行完畢后,棧幀被銷毀,通過恢復ebp和esp的值,釋放棧幀空間,將控制權返回給調用函數,繼續執行調用函數中調用之后的代碼

當函數調用結束后,前面創建的函數棧幀要銷毀,我們來看看Add函數的這部分反匯編代碼吧

005517C9  pop         edi  //在棧頂彈出一個值,存放到edi中,esp+4,向下移
005517CA  pop         esi  //在棧頂彈出一個值,存放到esi中,esp+4,向下移
005517CB  pop         ebx  //在棧頂彈出一個值,存放到ebx中,esp+4,向下移
005517CC  add         esp,0CCh  //esp+0cch,向下移
005517D2  cmp         ebp,esp  //esp的值給ebp,ebp來到了esp的位置
005517D4  call        00551253  
005517D9  mov         esp,ebp  //再將ebp的值給了esp,回收了Add函數的棧幀空間
005517DB  pop         ebp 
//彈出棧頂的值存放到ebp,棧頂此時的值恰好就是main函數的ebp,esp+4,恢復了main函數棧幀空間的維護
005517DC  ret 
//ret指令的執行,首先是從棧頂彈出一個值,此時棧頂的值就是call指令下一條指令的地址,此時esp+4,向下移動,然后直接跳轉到call指令下一條指令的地址處,繼續往下執行

回到了call指令下一條指令的地方

調用完后繼續回到main函數后繼續執行這兩串代碼,函數的返回值通過eax帶了出來,其中就是x+y的和z,也就是a+b的和c。


結語:本篇文章就到此結束了,對于函數棧幀的創建與銷毀,個人能力有限,歡迎大家進行補充,一起交流學習,感謝大家的支持!

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

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

相關文章

基于大模型預測的輸尿管癌診療全流程研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與創新點 二、大模型預測輸尿管癌的原理與方法 2.1 大模型技術概述 2.2 用于輸尿管癌預測的大模型選擇 2.3 數據收集與處理 2.4 模型訓練與優化 三、術前風險預測與手術方案制定 3.1 術前風險預測指標 3.2 大模型預測…

【Machine Learning Q and AI 讀書筆記】- 03 小樣本學習

Machine Learning Q and AI 中文譯名 大模型技術30講&#xff0c;主要總結了大模型相關的技術要點&#xff0c;結合學術和工程化&#xff0c;對LLM從業者來說&#xff0c;是一份非常好的學習實踐技術地圖. 本文是Machine Learning Q and AI 讀書筆記的第3篇&#xff0c;對應原…

PETR和位置編碼

PETR和位置編碼 petr檢測網絡中有2種類型的位置編碼。 正弦編碼和petr論文提出的3D Position Embedding。transformer模塊輸入除了qkv&#xff0c;還有query_pos和key_pos。這里重點記錄下query_pos和key_pos的生成 query pos的生成 先定義reference_points, shape為(n_query…

Ubuntu搭建 Nginx以及Keepalived 實現 主備

目錄 前言1. 基本知識2. Keepalived3. 腳本配置4. Nginx前言 ?? 找工作,來萬碼優才:?? #小程序://萬碼優才/r6rqmzDaXpYkJZF 爬蟲神器,無代碼爬取,就來:bright.cn Java基本知識: java框架 零基礎從入門到精通的學習路線 附開源項目面經等(超全)【Java項目】實戰CRU…

文章記單詞 | 第56篇(六級)

一&#xff0c;單詞釋義 interview /??nt?vju?/&#xff1a; 名詞&#xff1a;面試&#xff1b;采訪&#xff1b;面談動詞&#xff1a;對… 進行面試&#xff1b;采訪&#xff1b;接見 radioactive /?re?di???kt?v/&#xff1a;形容詞&#xff1a;放射性的&#xff…

MATLAB函數調用全解析:從入門到精通

在MATLAB編程中&#xff0c;函數是代碼復用的核心單元。本文將全面解析MATLAB中各類函數的調用方法&#xff0c;包括內置函數、自定義函數、匿名函數等&#xff0c;幫助提升代碼效率&#xff01; 一、MATLAB函數概述 MATLAB函數分為以下類型&#xff1a; 內置函數&#xff1a…

哈希表筆記(二)redis

Redis哈希表實現分析 這份代碼是Redis核心數據結構之一的字典(dict)實現&#xff0c;本質上是一個哈希表的實現。Redis的字典結構被廣泛用于各種內部數據結構&#xff0c;包括Redis數據庫本身和哈希鍵類型。 核心特點 雙表設計&#xff1a;每個字典包含兩個哈希表&#xff0…

PDF嵌入隱藏的文字

所需依賴 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-core</artifactId><version>9.0.0</version><type>pom</type> </dependency>源碼 /*** PDF工具*/ public class PdfUtils {/*** 在 PD…

RAG工程-基于LangChain 實現 Advanced RAG(預檢索-查詢優化)(下)

Multi-Query 多路召回 多路召回流程圖 多路召回策略利用大語言模型&#xff08;LLM&#xff09;對原始查詢進行拓展&#xff0c;生成多個與原始查詢相關的問題&#xff0c;再將原始查詢和生成的所有相關問題一同發送給檢索系統進行檢索。它適用于用戶查詢比較寬泛、模糊或者需要…

【業務領域】PCIE協議理解

PCIE協議理解 提示&#xff1a;這里可以添加系列文章的所有文章的目錄&#xff0c;目錄需要自己手動添加 PCIE學習理解。 文章目錄 PCIE協議理解[TOC](文章目錄) 前言零、PCIE掌握點&#xff1f;一、PCIE是什么&#xff1f;二、PCIE協議總結物理層切速 鏈路層事務層6.2 TLP的路…

Jupyter notebook快捷鍵

文章目錄 Jupyter notebook鍵盤模式快捷鍵&#xff08;常用的已加粗&#xff09; Jupyter notebook鍵盤模式 命令模式&#xff1a;鍵盤輸入運行程序命令&#xff1b;這時單元格框線為藍色 編輯模式&#xff1a;允許你往單元格中鍵入代碼或文本&#xff1b;這時單元格框線是綠色…

Unity圖片導入設置

&#x1f3c6; 個人愚見&#xff0c;沒事寫寫筆記 &#x1f3c6;《博客內容》&#xff1a;Unity3D開發內容 &#x1f3c6;&#x1f389;歡迎 &#x1f44d;點贊?評論?收藏 &#x1f50e;Unity支持的圖片格式 ??BMP:是Windows操作系統的標準圖像文件格式&#xff0c;特點是…

Spark-小練試刀

任務1&#xff1a;HDFS上有三份文件&#xff0c;分別為student.txt&#xff08;學生信息表&#xff09;result_bigdata.txt&#xff08;大數據基礎成績表&#xff09;&#xff0c; result_math.txt&#xff08;數學成績表&#xff09;。 加載student.txt為名稱為student的RDD…

內存安全的攻防戰:工具鏈與語言特性的協同突圍

一、內存安全&#xff1a;C 開發者永恒的達摩克利斯之劍 在操作系統內核、游戲引擎、金融交易系統等對穩定性要求苛刻的領域&#xff0c;內存安全問題始終是 C 開發者的核心挑戰。緩沖區溢出、懸空指針、雙重釋放等經典漏洞&#xff0c;每年在全球范圍內造成數千億美元的損失。…

OceanBase數據庫-學習筆記1-概論

多租戶概念 集群和分布式 隨著互聯網、物聯網和大數據技術的發展&#xff0c;數據量呈指數級增長&#xff0c;單機數據庫難以存儲和處理如此龐大的數據。現代應用通常需要支持大量用戶同時訪問&#xff0c;單機數據庫在高并發場景下容易成為性能瓶頸。單點故障是單機數據庫的…

計算機網絡——鍵入網址到網頁顯示,期間發生了什么?

瀏覽器做的第一步工作是解析 URL&#xff0c;分清協議是http還是https&#xff0c;主機名&#xff0c;路徑名&#xff0c;然后生成http消息&#xff0c;之后委托操作系統將消息發送給 Web 服務器。在發送之前&#xff0c;還需要先去查詢dns&#xff0c;首先是查詢緩存瀏覽器緩存…

Qwen3本地化部署,準備工作:SGLang

文章目錄 SGLang安裝deepseek運行Qwen3-30B-A3B官網:https://github.com/sgl-project/sglang SGLang SGLang 是一個面向大語言模型和視覺語言模型的高效服務框架。它通過協同設計后端運行時和前端編程語言,使模型交互更快速且具備更高可控性。核心特性包括: 1. 快速后端運…

全面接入!Qwen3現已上線千帆

百度智能云千帆正式上線通義千問團隊開源的最新一代Qwen3系列模型&#xff0c;包括旗艦級MoE模型Qwen3-235B-A22B、輕量級MoE模型Qwen3-30B-A3B。千帆大模型平臺開源模型進一步擴充&#xff0c;以多維開放的模型服務、全棧模型開發、應用開發工具鏈、多模態數據治理及安全的能力…

藍橋杯Python(B)省賽回憶

Q&#xff1a;為什么我要寫這篇博客&#xff1f; A&#xff1a;在藍橋杯軟件類競賽&#xff08;Python B組&#xff09;的備賽過程中我在網上搜索關于藍橋杯的資料&#xff0c;感謝你們提供的參賽經歷&#xff0c;對我的備賽起到了整體調整的幫助&#xff0c;讓我知道如何以更…

數據轉儲(go)

? 隨著時間推移&#xff0c;數據庫中的數據量不斷累積&#xff0c;可能導致查詢性能下降、存儲壓力增加等問題。數據轉儲作為一種有效的數據管理策略&#xff0c;能夠將歷史數據從生產數據庫中轉移到其他存儲介質&#xff0c;從而減輕數據庫負擔&#xff0c;提高系統性能&…