堆栈、栈帧、函数调用过程
2015-10-26 14:11
525 查看
一、堆和栈
首先,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。
程序对内存的使用分为以下几个区:
1、栈区(stack)— 由编译器自动分配释放 ,存放为运行函数而分配的局部变 量、函数参数、返回数据、返回地址等。 2、堆区(heap) — 一般由程序员分配释放, new, malloc之类的,若程序 员不释放,程序结束时可能由OS回收 。 3、全局区(静态区)(static)— 存放全局变量、静态数据、常量。程序结束 后由系统释放。 4、文字常量区 — 常量字符串就是放在这里的。程序结束后由系统释放。 5、程序代码区 — 存放函数体(类成员函数和全局函数)的二进制代码。
寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。下图为内存区域分配,观察栈在其中的位置:
堆和栈的申请方式:
栈由系统自动分配,速度较快,在windows下栈是向低地址扩展的数据结构,是一块连续的内存区域,大小是2MB。
堆需要程序员自己申请,并指明大小,速度比较慢。在C中用malloc,C++中用new。另外,堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机的虚拟内存。因此堆空间获取和使用比较灵活,可用空间较大。
1、入栈操作:
push eax; 等价于 esp=esp-4,eax->[esp];
如下图:
2、出栈操作:
pop eax; 等价于 [esp]->eax,esp=esp+4;
如下图:
二、栈帧结构和函数调用过程
栈在函数调用中的作用:参数传递、局部变量分配、保存调用的返回地址、保存寄存器以供恢复。
栈帧(stack Frame):
一次函数调用包括将数据和控制从代码的一个部分传递到另外一个部分,栈帧与某个过程调用 一一映射。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种 信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低址 地)。
函数调用规则:
(1)_cdecl:按从右至左的顺序压参数入栈,由调用者把参数弹出栈。由于每次函数调用都要 由编译器产生清楚堆栈的代码,所以使用_cdecl的代码比使用_stdcall的代码要大很多,但 是这种方式支持可变参数。对于C函数,名字修饰约定为在函数名前加下划线。对于C++,除非特 变使用extern C,C++使用不同的名字修饰方式。 (2)_stdcall:按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。调用约定在输出 函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数。 (3)_fastcall:主要特点就是快,因为它是通过寄存器来传送参数的,和__stdcall很 象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的, 即第一个参数进ECX,第2个进EDX,其他参数是从右向左的入stack。返回仍然通过EAX。
最后,以一个例子来解释函数调用过程:
void func(int param1 ,int param2,int param3) { int var1 = param1; int var2 = param2; int var3 = param3; printf("param1地址:0X%08X/n",¶m1); printf("param2地址:0X%08X/n",¶m2); printf("param3地址:0X%08X/n",¶m3); printf("var1地址: 0X%08X/n",&var1); printf("var2地址: 0X%08X/n",&var2); printf("var3地址: 0X%08X/n",&var3); } int main(int argc, char* argv[]) { func(1,2,3); return 0; }
运行结果如图:
下面分析调用过程:
在堆栈中变量分布是从高地址到低地址分布,EBP是指向栈底的指针,在过程调用中不变,又称为帧指针。ESP指向栈顶,程序执行时移动,ESP减小分配空间,ESP增大释放空间,ESP又称为栈指针。3个参数以从右向左的顺序压入堆栈,即从param3到param1,栈内分布如下图:
然后是返回地址入栈:此时的栈内分布如下:
通过跳转指令进入函数后,函数地址入栈后,EBP入栈,然后把当前ESP的值给EBP,汇编指令如下:
push ebp mov ebp esp
此时栈顶和栈底指向同一位置,栈内分布如下:
然后是
int var1 = param1; int var2 = param2; int var3 = param3;
按声明顺序依次存储。
转自:http://blog.csdn.net/zhongguoren666/article/details/7586074
三、例题:
int a=0; class someClass{ int b; static int c; }; int main(){ int d=0; someClass *p=new someClass(); return 0; }
关于以上代码中的变量在内存中的存储位置描述不正确的是()
A、b存在堆区
B、c存在堆区
C、d存在栈区
D、a存在全局变量区
正确答案:B
静态变量c肯定存放在静态区。对于b,是类的成员变量,由类的定义决定,在main函数中类A动态分配,因此b在堆区。!!!
相关文章推荐
- 应用领航:盘点那些年我们一起追过的OS
- 无奇不有!盘点各国自己开发的操作系统
- 可自定义oem的萝卜家园 Ghost XP 新春装机版 V200801 下载
- C#实现判断操作系统是否为Win8以上版本
- Linux操作系统添加新硬盘方法
- java如何获取本地操作系统进程列表
- Linux rdesktop操作系统下远程登录Windows XP桌面
- 32位操作系统认出超出4G内存的方法
- Linux rpm tar 操作系统下软件的安装与卸载方法
- JavaScript 获取用户客户端操作系统版本
- jsp 获取客户端的浏览器和操作系统信息
- Windows 操作系统的安全设置
- PHP获取用户的浏览器与操作系统信息的代码
- Perl操作系统环境变量的脚本代码
- javascript获取本机操作系统类型的方法
- 封装好的js判断操作系统与浏览器代码分享
- javascript实现获取浏览器版本、操作系统类型
- php根据操作系统转换文件名大小写的方法
- JS获得浏览器版本和操作系统版本的例子
- Python中使用异常处理来判断运行的操作系统平台方法