*函数的调用——栈帧*
来源:互联网 发布:mysql获取当前版本 编辑:程序博客网 时间:2024/05/21 15:05
1.函数调用
首先函数的存在是为了使代码模块化,逻辑清晰且便于查错、进行二次开发。那每一次的函数调用都是一个过程,这个过程我们称为:函数的调用过程。这个过程要为函数开辟栈空间,用于该函数调用中的临时变量的保存、现场保护,这块栈空间即为函数栈帧。
2.栈帧
编写如下代码:
#define _CRT_SECURE_NO_WARNINGS 1#include <Windows.h>#include <stdio.h>int add(int x, int y){int z = x + y;return z;}int main(){int a = 15;int b = 20;int c = add(a, b);printf("%d", c);system("pause");return 0;}
调试程序并进入反汇编有:
00931410 push ebp 00931411 mov ebp,esp 00931413 sub esp,0E4h 00931419 push ebx 0093141A push esi 0093141B push edi 0093141C lea edi,[ebp-0E4h] 00931422 mov ecx,39h 00931427 mov eax,0CCCCCCCCh 0093142C rep stos dword ptr es:[edi]
在程序执行时,main函数是程序的入口,但是_main函数不是第一个被调用的函数,第一个被调用的函数是_mainStartup函数,是它调用了main函数。ebp、esp是两个基址寄存器,分别用来存放函数栈帧栈底的地址、函数栈帧栈顶的地址。
int a = 15;0093142E mov dword ptr [ebp-4],0Fh int b = 20;00931435 mov dword ptr [ebp-8],14h
此时,_main函数被调用,同时为main函数创建了栈帧。将15即a放入ebp-4的地址,将20即b放入ebp-8的地址。此时的栈帧图如下:
int c = add(a, b);0093143C mov eax,dword ptr [ebp-8] 0093143F push eax 00931440 mov ecx,dword ptr [ebp-4] 00931443 push ecx 00931444 call @ILT+0(_add) (09310EBh) 00931449 add esp,8
将b放入eax寄存器中,且将eax压入栈中;将a放入ecx寄存器中,且将ecx压入栈中
call指令的功能:将当前正在执行指令的下一条指令地址压入栈中;随机跳(jmp)至指定函数
所以将地址00931449压入栈中(压栈操作同时伴随栈顶指针的改变),同时跳转至add函数,此时栈帧图如下:
int add(int x, int y){009313D0 push ebp 009313D1 mov ebp,esp 009313D3 sub esp,0CCh 009313D9 push ebx 009313DA push esi 009313DB push edi 009313DC lea edi,[ebp-0CCh] 009313E2 mov ecx,33h 009313E7 mov eax,0CCCCCCCCh 009313EC rep stos dword ptr es:[edi] int z = x + y;009313EE mov eax,dword ptr [ebp+8] 009313F1 add eax,dword ptr [ebp+0Ch] 009313F4 mov dword ptr [ebp-4],eax
此时函数add的栈帧也被创建,首先将寄存器ebp中的内容压入栈中,即将main函数的ebp中的内容压入栈中;再将寄存器esp中的内容放入寄存器ebp中,即栈底指针下移至栈顶指针处;再将esp减去0CCh,即栈顶指针下移,此时ebp、esp分别指向add函数的栈底和栈顶;再将ebp+8即a的内容放入eax中;再将a加上 ebp+0Ch处的b放入eax中;再将 eax的内容放入ebp-4中,此时栈帧图如下:
return z;009313F7 mov eax,dword ptr [ebp-4] }009313FA pop edi 009313FB pop esi 009313FC pop ebx 009313FD mov esp,ebp 009313FF pop ebp 00931400 ret
再将 ebp-4的内容放入eax中;将ebp的内容给esp,即将栈顶指针上移至栈底指针处(此时函数add的栈帧被销毁);pop将栈顶弹出且放入ebp中,即将ebp改变;ret的功能:将栈顶的值弹出,且将弹出的值放入eip,此时返回到main函数里。此时栈帧图如下:
00931449 add esp,8 0093144C mov dword ptr [c],eax
此时给esp加8,即将栈顶上移;将eax的内容放入 ebp-0Ch位置,即将z=a+b放入到了main函数的栈帧中,此时整个函数调用过程完成。此时栈帧图如下:
最终运行结果为:
- 栈帧——函数的调用过程
- 函数的调用过程——栈帧
- 函数的调用——栈帧
- *函数的调用——栈帧*
- 函数的调用过程—栈帧
- 深入理解函数的调用过程——栈帧
- Linux下的函数调用原理—栈帧
- JavaScript—函数的定义和调用
- 函数调用—sqrt函数
- 函数的调用过程--栈帧
- 函数调用原理——栈帧
- 函数调用过程——栈帧
- 函数调用方式——_stdcall与_cdecl的区别(函数调用方式)
- 利用函数指针实现动态库的动态调用——函数指针正向调用法
- C语言之函数调用10—重复函数的多次调用
- C语言之函数调用16—递归法之一般函数的调用(1)
- C语言之函数调用17—递归法之一般函数的调用(2)
- Python——调用函数
- 第十五周 排序 项目一 验证算法(1)冒泡排序
- 剑指offer 二叉树的下一个结点
- STM32的优先级NVIC_PriorityGroupConfig的理解及其使用
- linux下intel IPP 加密库安装及快速上手
- 图论之Watering Hole
- *函数的调用——栈帧*
- 虚拟机中设置Linux网络为静态IP踩过的坑
- Android开发中出现Attempt to invoke virtual method...on a null object reference
- Redis简介(一)概述
- Error: No network specified. Cannot determine current network异常
- 2000OJ_sort
- 通过nginx访问.html出现Access Denied提示怎么解决
- tensorflow 实现 正规方程求解线性回归
- 上机练习题——异常处理(课堂练习1)