从汇编的角度分析函数调用过程(1)

本文介绍了函数参数传递的两种方式:堆栈方式和寄存器方式,并详细解释了__cdecl、stdcall、PASCAL和fastcall等调用约定类型。通过具体的示例说明了不同调用约定下参数的传递顺序及堆栈平衡的方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 函数参数传递形式

函数的参数传递有2种方式:堆栈方式寄存器方式
如果是堆栈方式传递的,就需要定义函数参数在堆栈中的传递顺序,并约定函数被调用之后,由谁来平衡堆栈;
如果是寄存器方式传递的,就需要确定参数存放在哪个寄存器中。
每一种方式都有其优缺点,而且与使用的编程语言有关系,不存在哪种方式好与坏。

我们在开发中经常遇到调用约定类型,如__cdeclstdcallPASCALfastcall。这些调用约定类型就用来指定函数参数的传递方式的。上面几种约定类型,除了fastcall是使用寄存器方式传递参数外,其他的都是使用堆栈传递参数的。

Visual Studio中的C++工程,可以C++ –> 高级 –> 调用约定中进行调用约定的设置:
这里写图片描述

二. 使用堆栈方式传递函数参数

堆栈是一种“后进先出”的数据结构,ESP寄存器始终指向栈顶。栈中数据地址从底部到顶部依次减小,也就是说,栈底对应高地址,栈顶对应低地址。

调用函数时,调用者依次把参数压栈,然后调用函数,函数被调用之后,在堆栈中取得参数数据。函数调用结束以后,堆栈需要恢复到函数调用之前的样子,而到底是由调用者来恢复还是由函数自身来恢复,根据不同的调用约定类型采用不同的方式。

约定类型__cdeclstdcallPASCALfastcall
参数传递顺序从右到左从右到左从左到右使用寄存器
堆栈平衡者调用者函数自身函数自身函数自身

__cdcel是C/C++/MFC程序默认的调用约定。
stdcall是绝大多数Win32 API函数的约定方式,也有少部分使用__cdcel约定方式(如wsprintf等)。
在Windows C/C++开发中常用的就是__cdeclstdcall这2种调用约定。

按照不同的调用约定来调用函数int add(int a, int b)。从调用者的视角来看,其汇编代码分别表示如下:

__cdecl

push b     ;参数按从右到左传递
push a
call add
add esp, 8 ;调用者在函数外部平衡堆栈

stdcall

push b     ;参数按从右到左传递
push a
call add   ;函数自己内部平衡堆栈,调用者不需要平衡堆栈

在函数调用过程中,参数入栈的过程如图:
这里写图片描述

上图中,EBP函数返回地址ret都是32位地址。因为函数调用完之后会将EBP恢复为暂存在堆栈中的原EBP值,所以从调用者角度来看,在函数的一次调用过程中EBP是不会变化的。

我们可以在函数中通过新的EBP获取函数各个参数的值:

参数a = EBP + 0x8
参数b = EBP + 0xC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

china_jeffery

你的鼓励是我前进的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值