函數的棧幀

? ? ? ?我們每次在調用函數的時候,都說會進行傳參。每次創建函數,或者進行遞歸的時候,也會說會進行壓棧。

? ? ? ?那么,今天我們就來具體看看函數到底是如何進行壓棧,傳參的操作。

什么是棧?

? ? ? ?首先我們要知道,我們將內存一般劃分為三個區域:

  • 靜態區
  • 堆區
  • 棧區

? ? ? ?我們平時創建的臨時變量,函數都會在棧區中占據空間:

? ? ? ?此時我們也要知道棧區的使用規則:從高地址向低地址使用

棧的使用規則:

? ? ? ?我們知道搶的彈夾,我們要逐個把子彈往里面壓,之后如果取出子彈,就需要將上一次壓入的子彈取出,之后逐個取出子彈,并只能按照順序取出。

? ? ? ?棧就是這樣的使用規則,遵循先進后出,后進先出。? ? ? ? 此時你會想,不能把任意的數據取出,必須一個一個拿,這種結構真的好用嗎?

? ? ? ? 起初我也這樣認為,但是計算機就喜歡用這種結構。

? ? ? ? 在內存中,棧區的使用規則是從高地址向低地址使用的。

函數的棧幀:

? ? ? ?C語言中,我們要想觀察函數棧幀就需要用到調試。當我們調試時所在的函數(此時函數未運行完),每個棧幀對應著一個未運行完的函數。棧幀中保存了該函數的返回地址和局部變量。

? ? ? ?因為我們知道,只要運行函數就會進行壓棧操作,所以分析出一下信息:

  1. 棧幀是一塊因函數運行而創建的的臨時空間。
  2. 每調用一次函數都會創建一個獨立的函數棧幀。
  3. 棧幀中存放著函數重要信息,如局部變量,函數返回地址,函數參數等。
  4. 當函數運行完畢后棧幀會銷毀。

? ? ? ?既然會創建函數棧幀,那么就會維護其空間,計算機使用寄存器維護空間。

什么是寄存器?

? ? ? ?這里牽扯很多內容,我們只給出籠統解釋:寄存器是集成到CPU上的,是獨立的,寄存器可以暫存指令,地址和數據。所以寄存器也可以理解為指針。

? ? ? ?我們會使用很多寄存器,要理解清楚函數棧幀,就必須理解ebp和esp,這兩個寄存器中存放的是地址,這兩個地址是用來維護函數棧幀的。我們詳細來講esp和ebp兩個寄存器。其余寄存器混個臉熟即可,一會會用到。

esp寄存器:

? ? ? ?維護棧頂,始終指向棧頂(此時esp寄存器存儲棧頂地址)。

ebp寄存器:

? ? ? ?維護當前函數棧幀的棧低(此時ebp寄存器存儲函數棧幀棧低地址)。

幾個必要的匯編指令:

? ? ? ?我們觀察函數棧幀的創建和銷毀,就要知道幾個匯編指令,這樣可以更好的閱讀以下內容。

? ? ? ?記不住沒關系,我們一下會一一講解。?

圖解:?

? ? ? ? 這里我們使用VS2013來觀察,由于VS2022太過高級,有些內部細節就會看不到,所以用VS2013來觀察。此時我們執行以下代碼:

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;
}

? ? ? ?esp和ebp就是維護當前調用的函數。?

? ? ? ?點擊F10開始調試。? ? ? ?接下來我們看main函數被誰調用了,我們進入main函數,并直接執行完。

? ? ? ?在VS2013中,main函數也是被其他函數調用的。沒想到main函數也是被調用的函數,也理解了為什么每次都要有返回值。?

? ? ? ?mainCRTStartup函數調用 __tmainCRTStartup函數,__tmainCRTStartup函數調用main函數。

? ? ? ?之后我們按住F10,之后右擊鼠標找到“轉到反編匯”,就可以找到C語言所對應的匯編代碼,箭頭的指向就可以一行一行的執行,我們來逐過程分析:

? ? ? ?

? ? ? ? push ebp,將ebp壓入棧區。因為esp維護棧頂,所以esp指向改變。我們可以觀察其存放地址的改變。

? ? ? ?之后執行move,move是把后面的值賦到前面去。

? ? ? ?此時ebp和esp指向的位置相同。

? ? ? ? 之后,就要創建函數的棧幀了,執行sub,就是將其寄存器存放地址減去一個地址,因為棧區是高地址到底地址,所以該地址向上。

?

? ? ? ?此時esp維護main函數的棧頂,ebp維護main函數的棧低。我們可以看內存:

? ? ? ?這些內存都是為main函數開辟的空間。

? ? ? ?之后有執行了3次push,push時會有一個動作,就是棧頂指針esp會變,一直指向棧頂。

? ? ? ?我們通過內存窗口來觀察:? ? ? ? 因為我們說過,寄存器既可以存地址,也可以存數據,此時ebx存的是數據(就是內存里面的內容),而esp存放的是ebx的地址。壓入ebx以后,繼續將esi、edi壓入。

? ? ? ?之后執行lea : load effective address 加載有效地址。? ? ? ? 此時會發現,正好是加載的空間正好是最開始esp減去的空間,就是main函數棧幀的低地址。

? ? ? ?之后執行的命令,我們就需要先講解一下了。

? ? ? ?我們應該聽過字節的概念,1字節等于8比特位,那么字和字節又什么關系?一個字等于兩個字節。

? ? ? ?比特記為bit,字節記為Byte,字記為word,所以有如下關系:

  • 1Byte=8bits
  • 1word=2Bytes=16bits?
  • dword:一個word是兩個字節,d代表double,就是雙字,就是4個字節。

? ? ? ?此時我們要看其以下的三個步驟:

? ? ? ? 此時edi里面存放main函數棧幀的低地址。

? ? ? ??這樣就可以理解為什么每次打印未初始化的空間,打印出來的字符都是一個漢字“燙燙燙燙”了。

? ? ? ?此時才會開始執行有效的代碼。在此之前都是為main函數開辟的空間。

? ? ? ?此時就要調用Add函數了,一樣的,我們要改變ebp和esp的指向,因為進入Add函數就需要維護Add函數棧幀了,但是還是要做以下準備,就是傳參,我們來看形式參數的創建。

? ? ? ?這兩個動作相當于傳參,之后執行call,就是調用函數,要記住call的地址,此時點擊F11才能進入Add函數。

? ? ? ?我們可以發現就在ecx的下一個地址里面存儲了call指令下一個執行的地址。為什么要記錄地址?我們先埋個伏筆,此時我們會先進入Add函數,流程如下:

? ? ? ?注意此時main函數的函數棧幀已經增長到call指令的下一個地址了。? ? ? ? 此時我們來觀察Add函數的細節:將esp減去一個地址改變指向:

? ? ? ?之后還是main函數棧幀的那一套操作,壓入3個寄存器并初始化空間,并將z初始化為0:?

? ? ? ?此時先將 ebp + 8 的值賦給 eax ,此時 eax = 10;之后又執行add,將 ebp + 12 的值等于30,最后將eax的值賦給 ebp - 8 ,此時 ebp - 8 地址的值是30.? ? ? ? 我們可以發現,我們使用Add函數并沒有創建形參,在我們傳參時其實已經壓棧過了,而且參數是從右向左傳參的。

? ? ? ?返回的話z會被銷毀,我們來觀察其如何返回。

? ? ? ?我們將結果放入eax寄存器當中,此時就不用擔心函數銷毀。

? ? ? ?此時將上面的3個寄存器彈出棧頂。

? ? ? ?之后mov esp的位置,esp的指向改變:? ? ? ??此時彈出ebp,ebp彈出以后會指向main函數的棧低,因為之前記錄著mian函數的棧低。

?

? ? ? ??當前棧頂元素為call指令的下一個指令的地址,ret這條指令就是找到之前call指令記錄的地址,并pop一次棧頂元素。

? ? ? ??此時執行add ? esp,8 因為沒有dword 所以是改變指向。

? ? ? ?此時將形參x,y的空間還給操作系統。此時又執行mov,將eax存放的值賦給 ebp - 20h 就是給c賦值。?

? ? ? ?此時main函數執行完,也是以上步驟,我們不再贅述。

總結:?

? ? ? ?我們通過觀察函數棧幀的創建和銷毀,最后返回值是由寄存器帶回來的;也可以理解為什么局部變量的值是隨機的,形參和實參的關系,確實是一份臨時拷貝。希望大家下去多加練習,逐漸就會頓悟其中的原理。

? ? ? ?爆肝一整天,點點贊吧,嗚嗚~

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

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

相關文章

Error opening file for writing報錯解決

報錯展示及描述 在安裝pycharm的時候出現了一下報錯, Error opening file for writing。 報錯原因 一般出現這種報錯都是文件權限的原因,檢查一下,果然這個文件夾權限是【只讀】 查看文件權限的方式:【右擊】文件夾名稱&#xff0…

CSS結構偽類選擇器之否定偽類:not()

結構偽類選擇器是針對 HTML 層級結構的偽類選擇器。 常用的結構化偽類選擇器有: :root選擇器、:not選擇器、:only-child選擇器、:first-child選擇器、:last-child選擇器、 :nth-child選擇器、:nth-child(n)選擇器、:nth-last-child(n)選擇器、:nth-of-type(n)選擇…

046:vue通過axios調用json地址數據的方法

第046個 查看專欄目錄: VUE ------ element UI 專欄目標 在vue和element UI聯合技術棧的操控下,本專欄提供行之有效的源代碼示例和信息點介紹,做到靈活運用。 (1)提供vue2的一些基本操作:安裝、引用,模板使…

Userwindows pc電腦生成一個電腦唯一機器碼

在Windows PC上生成一個唯一標識碼通常涉及到計算機硬件和軟件的信息。有一些常見的方式可以獲取到一個較為唯一的標識碼: 1. 硬件信息: 可以通過獲取計算機的硬件信息來生成一個唯一標識碼。這可能包括CPU序列號、硬盤序列號、網卡MAC地址等。但請注意…

ffmpeg過濾器filter理論與實戰

文章目錄 前言一、DirectShow1、簡介2、程序基本結構3、架構 二、過濾器1、視頻過濾器 -vf2、音頻過濾器 -af3、過濾器鏈(Filterchain)4、過濾器圖(Filtergraph)①、基本語法②、Filtergraph 的分類 5、結構體間的關系圖 三、過濾…

Vue項目中WebSocket封裝

WEBSOCKET 封裝引入初始化使用 封裝 utils下建立WebSocketManager.js class WebSocketManager {constructor() {this.url null;this.websocket null;this.isConnected false;this.listeners {onopen: [],onmessage: [],onclose: [],onerror: [],};this.reconnectionOptio…

QML如何與C++層進行信號槽通訊

//QML端為槽函數 //其中serial為C類的對象 //CSerial serial(暫且可以這么理解) QML: Connections{ target: serial onStringReceived:{ console.log("receive:"receiveString) } } //C端為信號 //C //C類…

kafka 常用命令【學習筆記】

Kafka 環境變量配置 export KAFKA_HOME/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/kafka export PATH P A T H : PATH: PATH:KAFKA_HOME/bin 查看主題 ./kafka-topics.sh --list --zookeeper localhost:2181 創建主題 ./kafka-topics.sh --create --zook…

保研畢業論文查重率多少通過【保姆教程】

大家好,今天來聊聊保研畢業論文查重率多少通過,希望能給大家提供一點參考。 以下是針對論文重復率高的情況,提供一些修改建議和技巧: 保研畢業論文查重率多少通過 在保研過程中,畢業論文的查重率是衡量學術誠信和論文…

JAVA8新特性之函數式編程詳解

JAVA8新特性之函數式編程詳解 前言一、初步了解函數式接口二、 Lambda表達式2.1 概述2.2 lambda省略規則2.3 lambda省略常見實例2.4 lambda表達式與函數式接口 三、 Stream流3.1 stream流的定義3.2 Stream流的特點3.3 Stream流的三個步驟3.4 Stream 和 Collection 集合的區別&a…

【HarmonyOS開發】拖拽動畫的實現

動畫的原理是在一個時間段內,多次改變UI外觀,由于人眼會產生視覺暫留,所以最終看到的就是一個“連續”的動畫。UI的一次改變稱為一個動畫幀,對應一次屏幕刷新,而決定動畫流暢度的一個重要指標就是幀率FPS(F…

【帶頭學C++】----- 九、類和對象 ---- 9.12 C++之友元函數(9.12.1---12.4)

??????????????????????創做不易,麻煩點個關注???????????????????????? ??????????????????文末有驚喜!獻舞一支!???????????????????? 目錄 9.12…

TypeError: Cannot set properties of undefined (setting ‘xx‘)

在寫代碼中經常會遇到TypeError: Cannot set properties of undefined (setting ‘xx‘),這個問題。 一般出現的場景:在調用接口訪問后端數據時,前端渲染顯示空白,并報此錯。例如,我在調用高德地圖,輸入經…

五:爬蟲-數據解析之xpath解析

三:數據解析之xpath解析 1.xpath介紹: ? xpath是XML路徑語言,它可以用來確定xml文檔中的元素位置,通過元素路徑來完成對元素的查找,HTML就是XML的一種實現方式,所以xpath是一種非常強大的定位方式? XPa…

vue2 element-ui select下拉框 選擇傳遞多個參數

<el-select v-model"select" slot"prepend" placeholder"請選擇" change"searchPostFn($event,123)"> <el-option :label"item.ziDianShuJu" :value"{value:item.id, label:item.ziDianShuJu}" v-for&qu…

Ubuntu系統使用快速入門實踐(七)——軟件安裝與使用(5)

Ubuntu系統使用快速入門實踐系列文章 下面是Ubuntu系統使用系列文章的總鏈接&#xff0c;本人發表這個系列的文章鏈接均收錄于此 Ubuntu系統使用快速入門實踐系列文章總鏈接 下面是專欄地址&#xff1a; Ubuntu系統使用快速入門實踐系列文章專欄 文章目錄 Ubuntu系統使用快速…

crypto-js加密、解密與node Crypto加解密模塊的應用

前端用crypto-js實現加解密&#xff0c;node端用Crypto模塊&#xff0c;兩者想要相同結果的話&#xff0c;就要保持加密密鑰和加密算法一致。 crypto-js加密、解密 參考&#xff1a; 『crypto-js 加密和解密』 前端使用CryptoJS加密解密 // DES算法 import CryptoJS from cryp…

【unity】【WebRTC】從0開始創建一個Unity遠程媒體流app-構建可同步場景

【背景】 最近在研究遠程畫面&#xff0c;所以就實踐了一下。技術采用我認為比較合適的WebRTC。 這篇文章的基礎是我的另一篇博文&#xff0c;如果希望順利完成本篇操作&#xff0c;請先關注我后查詢我的如下博文&#xff1a; 【WebRTC】【Unity】Unity Web RTC1-Unity中簡單實…

Docker架構及常用的命令

一、初識Docker 1、 docker是一個快速交付應用、運行應用的技術&#xff0c;具備下列優勢&#xff1a; 可以將程序及其依賴、運行環境一起打包為一個鏡像&#xff0c;可以遷移到任意Linux操作系統運行時利用沙箱機制形成隔離容器&#xff0c;各個應用互不干擾啟動、移除都可以…

邊緣智能網關如何應對環境污染難題

隨著我國工業化、城鎮化的深入推進&#xff0c;包括大氣污染在內的環境污染防治壓力繼續加大。為應對環境污染防治難題&#xff0c;佰馬綜合邊緣計算、物聯網、智能感知等技術&#xff0c;基于邊緣智能網關打造環境污染實時監測、預警及智能干預方案&#xff0c;可應用于大氣保…