函数栈帧的创建和销毁

本文深入探讨C语言中局部变量的创建、初始化,函数参数传递机制,以及形参与实参的关系。通过代码示例和反汇编解析,展示了函数调用时栈帧的建立过程,详细解释了如何在内存中存储局部变量和函数调用参数。同时,分析了函数返回值的处理,强调了call指令在函数调用流程中的作用。
摘要由CSDN通过智能技术生成

·局部变量是怎么创建的?

·为什么局部变量的值是随机值?

·函数是怎么传参的?传参的顺序是怎样的?

·形参和实参是什么关系?

·函数调用是怎么做的?

·函数调用后是怎么返回的?

解决这些问题前我们先来写一串代码

#include <stdio.h>
​
int Add(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}
​
int main()
{
    int a = 10;
    int b = 20;
    int c = 0;
    
    c = Add(a, b);
    
    printf("%d", c);
    return 0;
}

eax、ebx、ecx、edx ebp、esp这些都是寄存器。ebp、esp这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的。每个函数的调用都要在栈区创建一个空间。

I:\系统默认\桌面\C语言课件V3-最新版\函数栈帧

当我们在vs2013测试运行这串代码的时候,打开调用堆栈的窗口,发现main函数又是被__tmain函数调用的

image-20210806165215473

image-20210806165339381

我们再转到反汇编代码中

#include <stdio.h>
​
int Add(int x, int y)
{
007613C0  push        ebp  
007613C1  mov         ebp,esp  
007613C3  sub         esp,0CCh  
007613C9  push        ebx  
007613CA  push        esi  
007613CB  push        edi  
007613CC  lea         edi,[ebp-0CCh]  
007613D2  mov         ecx,33h  
007613D7  mov         eax,0CCCCCCCCh  
007613DC  rep stos    dword ptr es:[edi]  //以上是在为Add函数调用栈帧
    int z = 0;
007613DE  mov         dword ptr [z],0  
    z = x + y;
007613E5  mov         eax,dword ptr [x]  //x放到eax中
007613E8  add         eax,dword ptr [y]  //y和eax相加
007613EB  mov         dword ptr [z],eax  //eax值放到z里
    return z;
007613EE  mov         eax,dword ptr [z]  //把z的值放到eax中去
}
007613F1  pop         edi  
007613F2  pop         esi  
007613F3  pop         ebx  
007613F4  mov         esp,ebp  
007613F6  pop         ebp  
007613F7  ret  //就是返回到call指令的下一条地址去
    ------------------------------------------------------------------
int main()
{
00761410  push        ebp  //push是压栈
00761411  mov         ebp,esp  //mov是把esp给ebp
00761413  sub         esp,0E4h  //esp地址减去0EAh
00761419  push        ebx  
0076141A  push        esi  
0076141B  push        edi  
0076141C  lea         edi,[ebp-0E4h]  //load effictive address是加载有效空间,把后边这个地址加载到edi中
00761422  mov         ecx,39h  //39h放到ecx中去
00761427  mov         eax,0CCCCCCCCh  //0ccccccch放到eax中去
0076142C  rep stos    dword ptr es:[edi]  //edi地址开始到39h这些空间都改成0ccccccch
    int a = 10;
0076142E  mov         dword ptr [ebp-8],0Ah  //把0Ah这个数字放到ebp-8中
    int b = 20;
00761435  mov         dword ptr [ebp-14h],14h  //把14h这个数字放到ebp-14h中
    int c = 0;
0076143C  mov         dword ptr [ebp-20h],0  //把0这个数字放到ebp-20h中
​
    c = Add(a, b);
00761443  mov         eax,dword ptr [ebp-14h]  //ebp-14h的值放到eax中去
00761446  push        eax  
00761447  mov         ecx,dword ptr [ebp-8]  //ebp-8的值放到ecx中去
0076144A  push        ecx  
0076144B  call        _Add (07610E1h)  
00761450  add         esp,8  //就是直接返回到这里
00761453  mov         dword ptr [ebp-20h],eax  
​
    printf("%d", c);
00761456  mov         esi,esp  
00761458  mov         eax,dword ptr [ebp-20h]  
0076145B  push        eax  
0076145C  push        765858h  
00761461  call        dword ptr ds:[769114h]  
00761467  add         esp,8  
0076146A  cmp         esi,esp  
0076146C  call        __RTC_CheckEsp (076113Bh)  
    return 0;
00761471  xor         eax,eax  
}
00761473  pop         edi  
00761474  pop         esi  
00761475  pop         ebx  
00761476  add         esp,0E4h  
0076147C  cmp         ebp,esp  
0076147E  call        __RTC_CheckEsp (076113Bh)  
00761483  mov         esp,ebp  
00761485  pop         ebp  
00761486  ret  

push是压栈操作,esp地址由原来位置上移,如图所示

 

image-20210806171428722

我们在监视窗口也可以看出esp地址发生变化

接下来就是mov是把esp给ebp,esp地址减去0EAh,

 

 

我们在内存窗口中可以看出这个空间是非常大的。

push ebx、esi、edi后如图所示

lea是load effective address是加载有效空间,把后边这个地址加载到edi中

mov把39h放到ecx中去0ccccccch放到eax中去 edi地址开始到39h这些空间都改成0ccccccch,如图所示

 

把0Ah这个数字放到ebp-8中 把14h这个数字放到ebp-14h中 把0这个数字放到ebp-20h中

原来的内存中放的是c,我们现在把10 20放到内存中,所以我们之前写代码的时候有时候会打印出来后边的烫烫烫汤乱码的现象,就是因为一开始存入的就是cccccc

当我们执行到call指令是就要开始调用Add函数了,此时如图所示

这一块代码是初始Add

如图所示

此时我们也会发现,a和b不是在Add函数栈帧里创建的,而是通过压栈回来找到的传参压进去的空间,所以我们之前说过形参是实参的一份临时拷贝是完全正确的

 

image-20210806182938809

接着执行代码,返回值。最后执行ret指令时其实就是返回到call指令的下一条地址去,所以我们当时栈中存了一个地址

image-20210806183629532

 如有错误,还请指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值