堆栈在函数运行时的简单解析
来源:互联网 发布:在校大学生贷款软件 编辑:程序博客网 时间:2024/06/05 05:49
写一个简单的求和函数简析堆栈在函数运行时所做的工作。
#define _CRT_SECURE_NO_DEPRECATE 1#include <stdio.h> #include <stdlib.h> int my_add(int a,int b){int sum=0; sum=a+b;return sum;}int main(){ int a=3; int b=2; int ret=0; ret=my_add(a,b); printf("%d\n",ret); system("pause");}
下面是mian函数对应的汇编代码
int main(){00A33D40 push ebp 00A33D41 mov ebp,esp 00A33D43 sub esp,0E4h 00A33D49 push ebx 00A33D4A push esi 00A33D4B push edi 00A33D4C lea edi,[ebp-0E4h] 00A33D52 mov ecx,39h 00A33D57 mov eax,0CCCCCCCCh 00A33D5C rep stos dword ptr es:[edi] int a=3;00A33D5E mov dword ptr [a],3 int b=2;00A33D65 mov dword ptr [b],2 int ret=0;00A33D6C mov dword ptr [ret],0 ret=my_add(a,b);00A33D73 mov eax,dword ptr [b] 00A33D76 push eax 00A33D77 mov ecx,dword ptr [a] 00A33D7A push ecx 00A33D7B call _my_add (0A311F9h) 00A33D80 add esp,8 00A33D83 mov dword ptr [ret],eax printf("%d\n",ret);
首先进入main函数:main函数是由_ _mainCRTstartup的函数调用的所以首先在栈里給这个函数划分一个区域
然后看汇编的第一句:push ebp (压栈 把ebp压入栈顶,同时esp向上移动)
接下来 mov ebp,esp (把esp的值給ebp, 即把ebp向上移动到esp的位置,这时esp,ebp指向同一块区域)
接下来 sub esp,0E4h (esp地址减去0x0E4h,即把esp低地址(向上)方向移动0E4h个内存单位)
接下来 push ebx
push esi
push edi (压栈)
push esi
push edi (压栈)
接下来 :
lea edi,[ebp-0E4h] (加载有效地址,把ebp减去0E4h的值給edi,最直观的就是edi指向了exb接下来的空间)
接下来: mov ecx,39h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi] (把39h給ecx,把0CCCCCCCCh 給eax,然后重复的做一件事,即从edi指向的地方开始把0CCCCCCCCh赋給接下来的地址空
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi] (把39h給ecx,把0CCCCCCCCh 給eax,然后重复的做一件事,即从edi指向的地方开始把0CCCCCCCCh赋給接下来的地址空
我们可以看看内存中的现象0x010FFD30即为edi指向。可以看出从edi后的39h个单元都被初始化位0xcccc
接下来:
int a=3;
mov dword ptr [a],3
int b=2;
mov dword ptr [b],2
int ret=0;
mov dword ptr [ret],0 ( 先把a放进栈里面,再把b放进栈里面最后把ret放进栈里面,栈时从高地址作用到低地址,先进后出,先进的在最底下)
int a=3;
mov dword ptr [a],3
int b=2;
mov dword ptr [b],2
int ret=0;
mov dword ptr [ret],0 ( 先把a放进栈里面,再把b放进栈里面最后把ret放进栈里面,栈时从高地址作用到低地址,先进后出,先进的在最底下)
我们再来看看内存里面的现象:
接下来: ret=my_add(a,b);
mov eax,dword ptr [b]
push eax
mov ecx,dword ptr [a]
push ecx (把b中存储的数給eax,然后eax压栈,把a中存储的数給ecx,然后ecx压栈 ,这是两个寄存器,这其实就是形参的拷贝)
mov eax,dword ptr [b]
push eax
mov ecx,dword ptr [a]
push ecx (把b中存储的数給eax,然后eax压栈,把a中存储的数給ecx,然后ecx压栈 ,这是两个寄存器,这其实就是形参的拷贝)
接下来 :call _my_add (011311F9h)
(执行到这一步,就会跳转进入我们写的my_ad这个子函数)
(执行到这一步,就会跳转进入我们写的my_ad这个子函数)
接下来:
01131470 push ebp 01131471 mov ebp,esp 01131473 sub esp,0C0h 01131479 push ebx 0113147A push esi 0113147B push edi 0113147C lea edi,[ebp-0C0h] 01131482 mov ecx,30h 01131487 mov eax,0CCCCCCCCh 0113148C rep stos dword ptr es:[edi]
我们在mian函数的上面再开辟一片空间,接下来这些步骤跟我们进入主函数时所做的开始工作时一模一样的从压栈ebp到0CCCCCCCCh 的初始化完成。 (图一直是从下到上连续的,截屏有限,最底下的截不上)
接下来:
int sum=0;
int sum=0;
我们来看看 内存中的变化:
第二次次初始化完成后我们发现第二次初始化后的结束空间很接近第一次初始化的开始时空间,这直接证明了每一次开辟新的栈都是在现有的栈上面的空间开辟,和我们图从底下开始画,像修高楼一样,越来越高。也直接证明了栈的作用方向是从高到低,从栈底开始,向栈顶作用。(栈底是高地址,栈顶是低地址)。
这是my_add下半段汇编代码:
这是my_add下半段汇编代码:
接下来:
sum=a+b;
mov eax,dword ptr [a]
add eax,dword ptr [b]
mov dword ptr [sum],eax (把a的值給eax,eax的值加上b赋值給eax,最后把eax的值赋給sum,其实就是把a+b赋給sum,所以执行完sum应该变为5)
sum=a+b;
mov eax,dword ptr [a]
add eax,dword ptr [b]
mov dword ptr [sum],eax (把a的值給eax,eax的值加上b赋值給eax,最后把eax的值赋給sum,其实就是把a+b赋給sum,所以执行完sum应该变为5)
我们来看看内存中的变化:
接下来
return sum;
mov eax,dword ptr [sum] (把sum的值給eax那么现在eax中的值就是5.)
接下来
pop edi
pop esi
pop ebx (edi,esi,ebx,出栈)
return sum;
mov eax,dword ptr [sum] (把sum的值給eax那么现在eax中的值就是5.)
接下来
pop edi
pop esi
pop ebx (edi,esi,ebx,出栈)
接下来:
mov esp,ebp (把ebp的值赋給esp,也就是把esp向下(高地址)移动,放弃之前所创建的my_add的栈区)
mov esp,ebp (把ebp的值赋給esp,也就是把esp向下(高地址)移动,放弃之前所创建的my_add的栈区)
接下来: pop ebp (ebp出栈,ebp回到main函数最开始的ebp处)
(my_add已经销毁)
(my_add已经销毁)
接下来执行 : ret (直接跳转,跳转回了主函数main里面)
接下来执行 add esp,8 (esp向下移动两个单位,销毁两个形参)
接下来执行
mov dword ptr [ret],eax (把eax的值交给ret,我们之前在子函数my_add最后把sum的值給了eax这个寄存器,而sum的值是形参3+2的值也就是5,从而这句话的意义就是把计算结果5赋值給ret)
我们来看内存中ret的值:
mov dword ptr [ret],eax (把eax的值交给ret,我们之前在子函数my_add最后把sum的值給了eax这个寄存器,而sum的值是形参3+2的值也就是5,从而这句话的意义就是把计算结果5赋值給ret)
我们来看内存中ret的值:
接下来:
printf("%d\n",ret); (相信这是最熟悉的一步了输出ret,程序执行到这一步我们逻辑步骤已经走完了)
剩下的都是像之前的移动esp,ebp来销毁栈,还包括一些系统的函数调用,这并不是重点,所以就不一一解释了。
printf("%d\n",ret); (相信这是最熟悉的一步了输出ret,程序执行到这一步我们逻辑步骤已经走完了)
剩下的都是像之前的移动esp,ebp来销毁栈,还包括一些系统的函数调用,这并不是重点,所以就不一一解释了。
最终达到esp和ebp都指向栈底时即所有栈空间都销毁完成,程序彻底结束。
我们可以注意到堆栈的创建是一个相反的过程,就好像时我们从1楼爬楼梯(创建堆栈),到了楼顶,做了该做的事情,然后又从楼梯下楼(销毁堆栈)。函数的功能就是在这创建堆栈和销毁堆栈的过程中实现的。
我们可以注意到堆栈的创建是一个相反的过程,就好像时我们从1楼爬楼梯(创建堆栈),到了楼顶,做了该做的事情,然后又从楼梯下楼(销毁堆栈)。函数的功能就是在这创建堆栈和销毁堆栈的过程中实现的。
0 0
- 堆栈在函数运行时的简单解析
- 一个简单的使用C++在运行时获取调用堆栈的类
- [原创]一个简单的使用C++在运行时获取调用堆栈的类
- 一个简单的使用C++在运行时获取调用堆栈的类
- 简单解析Lua的堆栈
- 函数调用堆栈的汇编解析
- delphi下使用jclDebug,在运行时显示详细的调试堆栈信息的范例
- c/c++运行期库的堆栈检查函数
- 多线程运行递归函数导致堆栈溢出的问题
- 运行时堆栈的分析图
- 【Cocos2d-x lua篇003】简单解析Lua的堆栈
- 解析C++对象在堆栈区的析构
- 对堆栈的解析
- 堆栈问题的解析
- 构造函数的简单解析
- 堆栈的简单实现
- 堆栈的简单介绍
- 简单解析PHP程序的运行流程
- 全面理解Java内存模型
- maven项目改名
- 《Training:Sending Simple Data to Other Apps》
- linux 几个常用的命令整理
- request获取表单 Map
- 堆栈在函数运行时的简单解析
- Python 图像转化为文档标签
- 我门常用的一些软件使用的正确姿态
- 坐在马桶上看算法:快速排序
- FileProvider无法获取外置SD卡问题解决方案 | Failed to find configured root that contains(转)
- 搭建spring 和maven工程时发生警告No mapping found for HTTP request with URI
- 表格、矩形、正方形、类微信图文【瀑布流排版】列表jQuery分页插件
- hdu 2211 杀人游戏
- activation函数小解