在学习C语言时,我们难免有许多疑问
(1)局部变量是怎么创建的?
(2)为什么局部变量的值是随机的?
(3)函数是怎么传参的?传参的顺序如何?
(4)形参和实参是什么关系?
(5)函数调用是怎么做的?
(6)函数调用结束后是怎么返回的?
接下来带你学习函数栈帧。
在vs底下各个版本略有差异,而今天带你学习的是vs2013
寄存器
一般计算机内
通用寄存器
包括eax,ebx,ecx,edx,esi,edi,esp,edp,其中
esp,ebp
这两个寄存器是用来存放地址的,这两个地址就是用来维护函数栈帧的
函数栈帧介绍
写一个简单的代码来详细介绍
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;
}
每一个函数调用,都要在栈区创建一个空间
而esp和ebp是来维护函数栈帧的
esp:栈顶指针
ebp:栈底指针
调试:
mainCRTStartup 函数调用 _tmainCRTStartup函数,
而 __tmainCRTStartup 函数调用 main 函数。
右击鼠标可以看到反汇编,就可以看到C语言的汇编代码:
push 压栈:给栈顶放上一个元素
pop 出栈:从栈顶删除一个元素
首先给__tmainCRTStartup开辟一块内存空间
然后mov,把esp给ebp。sub 给 esp 减去0E4h,而这块空间就是给mian函数开辟的空间。
接下来push压栈 ebx,esi,edi。
然后lea:load effective address(加载的意思)。把ebp-0E4h地址给edi。
然后mov,把39h给ecx,然后把ecx改成0CCCCCCCCh。
rep stos的39h个dword(4个字节)全部初始化0CCCCCCCCh。
到 int a=10;就是把0Ah(10的十六进制就是a)放到[ebp-8]
int b=20;把14h放到[ebp-14h]
int c=0;把0放到[ebp-20h]
Add(a,b);
mov,把ebp-14h给eax。然后push(压栈)eax。
mov,把ebp-8给ecx。然后push(压栈)ecx。
call指令调用函数(call指令的下一条指令进行压栈(记住),等调用完函数就会回来找到这个地址)
int Add(int x,int y);
为Add函数准备
z=x+y;
mov,把[ebp+8]给eax(值是10,前面已经传过来了)。
add,把[ebp+0Ch]的值(20)加给eax。
mov,把eax的值给ebp-8。
从而可以知道形参根本不是函数内部创建的,而是call指令回来找到push的eax和ecx。
所以形参是实参的一份临时拷贝。
return z;
把[ebp-8]给eax寄存器,最后把edi,esi,ebx pop(弹出)。
空间回收,把ebp给esp。pop(弹出) ebp
ret
结束找回call指令的下一条指令,然后[esp+8]指向edi,形参销毁。eax给ebp-20h
版权归原作者 strbug 所有, 如有侵权,请联系我们删除。