在c语言中编译的程序占用的内存分为一下几个部分:
1、栈区(stack)2、堆区(heap)3、全局区(静态区)(static)4、字符常量区 5、代码区(code)
如图所示:
这篇博客主要描述其中的栈区。
在函数的调用过程中,要为函数开辟空间,用于本次函数的调用中临时变量保存、现场保护。这块栈空间我们称之为函数的栈帧。
先要了解 几个寄存器 : ESP 栈顶寄存器 EBP栈底寄存器(基址寄存器) EIP 程序计数器:放当前正在执行指令的下一条指令的内容
以一个简单的程序来描述函数的栈帧。
#include <stdio.h>
#include <windows.h>
int Add(int x,int y)
{
int z=0;
z=x+y;
return z;
}
int main()
{
int a=0xaaaaaaaa;
int b=0xbbbbbbbb;
int ret=Add(a,b);
printf("ret=%d\n",ret);
system("pause");
return 0;
}
分段转入此程序的汇编代码中:
在此段代码中,栈结构如图所示。 在此图中,我们发现函数形参实例化是从右向左的。
将ecx中内容入栈,接来下就是call命令,call命令有两个作用:
①将当前汇编地址的下一条地址保存在栈上,便于恢复。
②跳转至目标函数的入口地址处。
call命令后,由汇编代码可以看到,跳到了 目标地址 00401005。
此段汇编代码,则是函数内部的调用过程。
对应栈上的图,相对比较好理解。
此时函数的栈帧已经形成,就是 粗体EIP,ESP所指向的内容。
接下来就是ret指令,有两个功能:
① 将之前保存的函数值的返回地址进行出栈,即00401093出栈。
② 将弹出的地址放入EIP。
按下F11后,我们跳转至了 00401093,如图:
也就是 此时ESP指向红线处:
经过此代码,函数调用过程中产生的临时变量全部清空,即临时变量随用随丢,由图可证。
函数调用完成,返回值也附给main函数。
进过函数调用产生的栈帧,亦可以得出两个结论。
一、函数形参形成是随用随丢的。
二、函数形参调用是从右至左形成的。
仅以个人理解作此文章,难免有遗漏或不足之处,欢迎随时指出问题!