函数调用与返回的相关指令

简介: 理解函数调用与返回的相关汇编指令及递归函数

如有以下code demo:

include

int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int c = max(3,4);
return 0;
}
函数调用和返回会分别用到call和ret两个汇编指令。

8: int c = max(3,4);
00401088 push 4
0040108A push 3
0040108C call @ILT+0(max) (00401005)
00401091 add esp,8
00401094 mov dword ptr [ebp-4],eax
……………………………………………………………………………………
@ILT+0(?max@@YAHHH@Z):
00401005 jmp max (00401020)
……………………………………………………………………………………
2: int max(int a,int b)
3: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
4: return a>b?a:b;
00401038 mov eax,dword ptr [ebp+8]
0040103B cmp eax,dword ptr [ebp+0Ch]
0040103E jle max+28h (00401048)
00401040 mov ecx,dword ptr [ebp+8]
00401043 mov dword ptr [ebp-4],ecx
00401046 jmp max+2Eh (0040104e)
00401048 mov edx,dword ptr [ebp+0Ch]
0040104B mov dword ptr [ebp-4],edx
0040104E mov eax,dword ptr [ebp-4]
5: }
00401051 pop edi
00401052 pop esi
00401053 pop ebx
00401054 mov esp,ebp
00401056 pop ebp
00401057 ret
1 call addr
Switches active frame to callee function. Equivalent to:

push %eip // eip保存指令目标地址

mov addr, %eip
//代码效果参考:http://www.zidongmutanji.com/bxxx/27882.html

2 ret
Restores active frame to caller function. Equivalent to:

pop %eip

(每执行一个指令,都会更新%eip的值。)

3 push S
Pushes a copy of S onto the top of the stack. Equivalent to:

sub $4, %esp

mov S, (%esp)

4 pop D
Pops the top element off the stack and places it in location D. Equivalent to:

mov (%esp), D

add $4, %esp

(push、pop、call、ret都会更新%esp的值,也就是栈指针偏移。)

5 递归函数
先看一个while循环:

int sumDown(int n) { // iterative version
int total = 0;
int i = n;
while (i > 0) {
total += i;
i--;
}
return total;
}
两个汇编跳转指令构成一个while循环:

6: while (i > 0) { // iterative version
00401045 cmp dword ptr [ebp-8],0
00401049 jle sumDown+3Fh (0040105f) // 条件跳转至return前
7: total += i;
0040104B mov ecx,dword ptr [ebp-4]
0040104E add ecx,dword ptr [ebp-8]
00401051 mov dword ptr [ebp-4],ecx
8: i--;
00401054 mov edx,dword ptr [ebp-8]
00401057 sub edx,1
0040105A mov dword ptr [ebp-8],edx
9: }
0040105D jmp sumDown+25h (00401045) // 无跳转跳转回while语句:
10: return total;
0040105F mov eax,dword ptr [ebp-4]
11: }
将while改写为goto版本:

int sumIf(int n){ // goto version
int total = 0;
int i = n;
restart:
if(i<=0)
goto end;
total +=i;
i--;
goto restart;
end:
return total;
}
对应的汇编同样由几个跳转汇编指令构成:

6: restart:
7: if(i<=0)
00401095 cmp dword ptr [ebp-8],0
00401099 jg restart+8 (0040109d)
8: goto end;
0040109B jmp end+5 (004010b6)
9: total +=i;
0040109D mov ecx,dword ptr [ebp-4]
004010A0 add ecx,dword ptr [ebp-8]
004010A3 mov dword ptr [ebp-4],ecx
10: i--;
004010A6 mov edx,dword ptr [ebp-8]
004010A9 sub edx,1
004010AC mov dword ptr [ebp-8],edx
11: goto restart;
004010AF jmp restart (00401095)
12: end:
13: return total;
004010B1 mov eax,dword ptr [ebp-4]
004010B4 jmp end+7 (004010b8)
14: }
004010B6 jmp end (004010b1)
004010B8 pop edi
递归版本:

int sumr(int n) {
if (n <= 0) {
return 0;
}
return n + sumr(n-1);
}
对应的汇编:

13: int c = sumr(36);
0040D4C8 push 24h
0040D4CA call @ILT+35(sumr) (00401028) // call启动
………………………………………………………………………………
4: if (n <= 0) {
00401088 cmp dword ptr [ebp+8],0
0040108C jg sumr+22h (00401092)
5: return 0;
0040108E xor eax,eax
00401090 jmp sumr+36h (004010a6) // call 结束,ret 开始
6: }
7: return n + sumr(n-1); // 按条件call,且参数构成迭代:n = n-1
00401092 mov eax,dword ptr [ebp+8]
00401095 sub eax,1
00401098 push eax
00401099 call @ILT+30(sumDownIf) (00401023)
0040109E add esp,4
004010A1 mov ecx,dword ptr [ebp+8]
004010A4 add eax,ecx
8: }
004010A6 pop edi
004010A7 pop esi
004010A8 pop ebx
004010A9 add esp,40h
004010AC cmp ebp,esp
004010AE call __chkesp (004010c0)
004010B3 mov esp,ebp
004010B5 pop ebp
004010B6 ret // ret
迭代版本同goto版本一样,通过条件判断不断构成跳转,形成循环:

call……call……call…………ret……ret……ret…………

在不断的call中,参数可以构成迭代:n = n-1

在不断的ret中,返回结果存储在%eax寄存器(在非递归版本中使用临时变量total保存)。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
Ubuntu Linux
百度搜索:蓝易云【Ubuntu更改默认启动内核】
请注意,更改默认启动内核可能会影响系统的稳定性和性能。在更改前,请确保选择的内核版本与你的系统硬件和配置兼容。如果遇到问题,你可以在GRUB启动菜单中选择其他内核来启动系统,并在问题解决后再将默认内核设置为你选择的版本。
368 0
|
SQL 开发框架 .NET
ASP.NET连接SQL数据库:详细步骤与最佳实践指南ali01n.xinmi1009fan.com
随着Web开发技术的不断进步,ASP.NET已成为一种非常流行的Web应用程序开发框架。在ASP.NET项目中,我们经常需要与数据库进行交互,特别是SQL数据库。本文将详细介绍如何在ASP.NET项目中连接SQL数据库,并提供最佳实践指南以确保开发过程的稳定性和效率。一、准备工作在开始之前,请确保您
852 3
|
消息中间件 安全 Kafka
Kafka启动后需要开放什么端口?
Kafka启动后需要开放什么端口?
4726 7
|
算法 程序员 开发工具
C语言编程规范
C 语言编程规范有助于提升代码的可读性、可维护性和可移植性。主要包括:命名规范(如 `my_variable`、`MAX_SIZE`)、代码缩进与空格、注释(解释逻辑但不过度)、函数设计(短小精悍、参数不超过三个)、错误处理、避免魔法数字、选择合适数据结构、使用标准库、保持代码格式一致及版本控制。遵循这些规范能显著提高团队开发效率和代码质量。
1633 2
|
存储 弹性计算 数据处理
阿里云对象存储OSS怎么收费?包年包月和按量付费价格表
阿里云对象存储OSS提供灵活的计费方案,包括存储费、流量费和请求费等。用户可选择按量付费或包年包月模式。标准型存储按量付费为0.09元/GB/月,包年包月则有多种套餐选择,如9元/年40GB和99元/年100GB。OSS流量费仅针对公网出方向,并区分闲忙时段。此外还提供流量包服务。更多详情及特殊需求费用(如数据处理、传输加速等)
|
数据采集 JSON Serverless
通过百炼大模型+FC函数计算构建小红书图文工作流
使用阿里云函数服务和百炼平台,快速构建小红书图文创作工作流。通过两步轻松创建: 1) 在函数计算中利用Puppeteer构建卡片生成服务; 2) 在百炼平台上创建工作流,整合大模型、脚本和函数计算节点,实现图文内容的自动化处理和生成。此方案适合高效创作小红书内容。
2429 7
|
机器学习/深度学习 自然语言处理 语音技术
【论文速递】COLING2022 - 因果情绪关联的双流注意力模型
【论文速递】COLING2022 - 因果情绪关联的双流注意力模型
|
消息中间件 负载均衡 Kafka
Kafka - 3.x 分区分配策略及再平衡不完全指北
Kafka - 3.x 分区分配策略及再平衡不完全指北
850 0
[simulink] --- simulink中stateflow的使用(上)
[simulink] --- simulink中stateflow的使用
712 0