網站首頁 編程語言 正文
?一、函數調用約定
- _cdecl:C調用約定
- _stdcall:Windows標準的調用約定
- _fastcall:快速調用約定
- _thiscall:C++的成員函數調用約定
以上的函數調用約定入參都是從右向左,只有PASCAL從左向右
函數調用約定不同,會影響函數生成的符號名,函數入參順序,形參內存的清理者
1. 影響函數生成的符號名
在一個文件中寫_cdecl的函數聲明:
在另一個文件中寫_stdcall的函數定義:
我們編譯鏈接一下:
鏈接器找不到__cdecl sum這個函數調用的定義,將聲明的地方改成__stdcall就可以鏈接成功
2. 影響形參內存的釋放者
_stdcall
形參內存還是由調用方開辟
ret
表示把棧頂元素的值(調用處下一條指令的地址)賦給PC寄存器,并出棧棧頂元素(修改esp)ret 8
表示在ret操作的基礎上,執行執行指令add esp, 8
_fastcall
可以看到在_fastcall調用約定中,call指令前面并沒有push操作,而是通過寄存器把實參傳遞到形參(沒有壓棧出棧,速度很快),實參在調用方棧幀上,形參在被調用方棧幀上
在_fastcall調用約定中,最多只能通過寄存器將最左邊8字節的實參帶給形參,多于8字節的實參還是通過push的方式帶給調用方的形參
我們給sum傳入三個參數,看看是誰釋放形參內存 :
這就很清楚了,一共3個參數,左邊的2個參數通過寄存器傳遞不需要清理內存,只有一個形參內存需要釋放,所以顯示ret 4
對于sum函數的第一個局部變量temp,在_cdecl和_stdcall中都是通過ebp-4訪問的,形參是通過ebp正向偏移訪問,因為形參內存在調用方的棧幀上
而在_fastcall中是通過寄存器把左邊的8字節實參帶給sum的形參,并存放在sum函數的棧幀上:
mov edx, dword ptr [ebp-8] mov ecx, dword ptr [ebp-4]
所以對于sum函數的第一個局部變量temp,只能通過ebp-0Ch訪問
_thiscall
對參數個數不確定的,調用者清理堆棧,否則被調用者清理堆棧
二、函數的返回值
函數的返回值分為內置類型(char、short、int、long、float、double等)、結構體類型、union、enum等
1. 0 < 返回值 <= 4字節
通過eax寄存器帶出
2. 4字節 < 返回值 <= 8字節
#include <stdio.h> typedef struct { int a; int b; }Data; Data sum(Data a, Data b) { Data temp = { 0 }; temp.a = a.a + b.a; return temp; } int main() { Data a = { 10 }; Data b = {20}; Data ret = { 0 }; ret = sum(a, b); return 0; }
可以看到,4字節 < 返回值 <= 8字節時,通過eax和edx寄存器帶出
3. 返回值 > 8字節
#include <stdio.h> typedef struct { int a[20]; }Data; Data sum(Data a, Data b) { Data temp = { 0 }; temp.a[0] = a.a[0] + b.a[0]; return temp; } int main() { Data a = { 10 }; Data b = {20}; Data ret = { 0 }; ret = sum(a, b); return 0; }
壓參數的時候,沒有使用push指令,因為寄存器不夠用,故使用了循環拷貝的方法,從實參的空間拷貝到形參的空間
產生臨時量有三個地方:函數調用前,函數調用時return的地方,函數調用完成時。在接收大于8字節返回值時,是在函數調用前產生臨時量,并把臨時量內存的地址壓棧,而這個臨時量是用來接收返回值的
我們看到不僅僅壓棧了實參a、b,還壓棧了臨時量的地址,可以把sum函數簡單理解為如下形式:
Data sum(void* tmp_address, Data a, Data b);
我們看一下sum函數中return時的匯編指令是如何待會80字節的返回值的
最后通過eax把臨時量的地址帶出來,調用函數就可以通過eax拿到sum函數的返回值了
如果臨時量在函數調用前產生,那被調用函數返回的時候,肯定是通過ebp+8
訪問臨時量并寫入返回值。因為ebp指向的空間保存了調用函數的棧底地址,ebp+4指向的空間保存了call指令下一條指令的地址,ebp+8指向最后一個壓棧的實參,即用于帶出返回值的臨時量的地址
返回方式:
- 返回值空間在[1,4],用eax寄存器
- 返回值空間在[5,8],用eax、edx寄存器
- 返回值空間大于8字節時,函數調用前產生臨時量用于存儲返回值,并把這個臨時量的地址作為最后一個實參壓棧,在被調用函數中通過
ebp+8
訪問該臨時量的地址
原文鏈接:https://blog.csdn.net/qq_42500831/article/details/124432347
相關推薦
- 2022-10-17 Go?WaitGroup及Cond底層實現原理_Golang
- 2022-12-22 數據清洗之如何用一行Python代碼去掉文本中的各種符號_python
- 2022-11-16 python?中collections的?deque使用詳解_python
- 2023-02-12 React實現錨點跳轉組件附帶吸頂效果的示例代碼_React
- 2022-09-26 你了解Redis事務嗎_Redis
- 2022-04-02 Python+Tkinter繪制一個數字時鐘_python
- 2022-09-29 OpenCV中Grabcut算法的具體使用_C 語言
- 2022-01-18 利用css3實現立體旋轉動畫效果
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支