我是靠谱客的博主 温暖玉米,这篇文章主要介绍堆栈、栈帧、函数调用过程,现在分享给大家,希望可以做个参考。

一、堆和栈

首先,栈是从高地址向低地址延伸的。

每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。

程序对内存的使用分为以下几个区:

复制代码
1
2
3
4
5
6
7
8
1、栈区(stack)— 由编译器自动分配释放 ,存放为运行函数而分配的局部变 量、函数参数、返回数据、返回地址等。 2、堆区(heap) — 一般由程序员分配释放, new, malloc之类的,若程序 员不释放,程序结束时可能由OS回收 。 3、全局区(静态区)(static)— 存放全局变量、静态数据、常量。程序结束 后由系统释放。 4、文字常量区 — 常量字符串就是放在这里的。程序结束后由系统释放。 5、程序代码区 — 存放函数体(类成员函数和全局函数)的二进制代码。

寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。下图为内存区域分配,观察栈在其中的位置:
这里写图片描述

堆和栈的申请方式:

栈由系统自动分配,速度较快,在windows下栈是向低地址扩展的数据结构,是一块连续的内存区域,大小是2MB。
堆需要程序员自己申请,并指明大小,速度比较慢。在C中用malloc,C++中用new。另外,堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机的虚拟内存。因此堆空间获取和使用比较灵活,可用空间较大。

1、入栈操作:

复制代码
1
push eax; 等价于 esp=esp-4,eax->[esp];

如下图:
这里写图片描述

2、出栈操作:

复制代码
1
pop eax; 等价于 [esp]->eax,esp=esp+4;

如下图:
这里写图片描述

二、栈帧结构和函数调用过程

栈在函数调用中的作用:

复制代码
1
参数传递、局部变量分配、保存调用的返回地址、保存寄存器以供恢复。

栈帧(stack Frame):

复制代码
1
2
3
4
一次函数调用包括将数据和控制从代码的一个部分传递到另外一个部分,栈帧与某个过程调用 一一映射。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种 信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低址 地)。

函数调用规则:

复制代码
1
2
3
4
5
6
7
8
9
1)_cdecl:按从右至左的顺序压参数入栈,由调用者把参数弹出栈。由于每次函数调用都要 由编译器产生清楚堆栈的代码,所以使用_cdecl的代码比使用_stdcall的代码要大很多,但 是这种方式支持可变参数。对于C函数,名字修饰约定为在函数名前加下划线。对于C++,除非特 变使用extern CC++使用不同的名字修饰方式。 (2)_stdcall:按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。调用约定在输出 函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数。 (3)_fastcall:主要特点就是快,因为它是通过寄存器来传送参数的,和__stdcall很 象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的, 即第一个参数进ECX,第2个进EDX,其他参数是从右向左的入stack。返回仍然通过EAX。

最后,以一个例子来解释函数调用过程:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void func(int param1 ,int param2,int param3) { int var1 = param1; int var2 = param2; int var3 = param3; printf("param1地址:0X%08X/n",&param1); printf("param2地址:0X%08X/n",&param2); printf("param3地址:0X%08X/n",&param3); 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,汇编指令如下:

复制代码
1
2
push ebp mov ebp esp

此时栈顶和栈底指向同一位置,栈内分布如下:
这里写图片描述

然后是

复制代码
1
2
3
int var1 = param1; int var2 = param2; int var3 = param3;

按声明顺序依次存储。
这里写图片描述

转自:http://blog.csdn.net/zhongguoren666/article/details/7586074

三、例题:

复制代码
1
2
3
4
5
6
7
8
9
10
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在堆区。!!!

最后

以上就是温暖玉米最近收集整理的关于堆栈、栈帧、函数调用过程的全部内容,更多相关堆栈、栈帧、函数调用过程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(89)

评论列表共有 0 条评论

立即
投稿
返回
顶部