ebp 函数堆栈esp_堆栈你真的了解吗?C/C++堆栈工作机制

来源:https://segmentfault.com/a/1190000038292644

前言

我们经常会讨论这样的问题:什么时候数据存储在堆栈 (Stack) 中,什么时候数据存储在堆 (Heap) 中。我们知道,局部变量是存储在堆栈中的;debug 时,查看堆栈可以知道函数的调用顺序;函数调用时传递参数,事实上是把参数压入堆栈,听起来,堆栈象一个大杂烩。那么,堆栈 (Stack) 到底是如何工作的呢?本文将详解 C/C++ 堆栈的工作机制。阅读时请注意以下几点:

1)本文讨论的编译环境是 Visual C/C++,由于高级语言的堆栈工作机制大致相同,因此对其他编译环境或高级语言如 C# 也有意义。

2)本文讨论的堆栈,是指程序为每个线程分配的默认堆栈,用以支持程序的运行,而不是指程序员为了实现算法而自己定义的堆栈。

3) 本文讨论的平台为 intel x86。

4)本文的主要部分将尽量避免涉及到汇编的知识,在本文最后可选章节,给出前面章节的反编译代码和注释。

5)结构化异常处理也是通过堆栈来实现的(当你使用 try…catch 语句时,使用的就是 c++ 对 windows 结构化异常处理的扩展),但是关于结构化异常处理的主题太复杂了,本文将不会涉及到。

从一些基本的知识和概念开始

1) 程序的堆栈是由处理器直接支持的。在 intel x86 的系统中,堆栈在内存中是从高地址向低地址扩展(这和自定义的堆栈从低地址向高地址扩展不同),如下图所示:

35f3a922c3e38b6d5939f89d17cadddc.png

因此,栈顶地址是不断减小的,越后入栈的数据,所处的地址也就越低。

2) 在 32 位系统中,堆栈每个数据单元的大小为 4 字节。小于等于 4 字节的数据,比如字节、字、双字和布尔型,在堆栈中都是占 4 个字节的;大于 4 字节的数据在堆栈中占4字节整数倍的空间。

3) 和堆栈的操作相关的两个寄存器是 EBP 寄存器和 ESP 寄存器的,本文中,你只需要把 EBP 和 ESP 理解成 2 个指针就可以了。ESP 寄存器总是指向堆栈的栈顶,执行 PUSH 命令向堆栈压入数据时,ESP减4,然后把数据拷贝到ESP指向的地址;执行POP 命令时,首先把 ESP 指向的数据拷贝到内存地址/寄存器中,然后 ESP 加 4。EBP 寄存器是用于访问堆栈中的数据的,它指向堆栈中间的某个位置(具体位置后文会具体讲解),函数的参数地址比 EBP 的值高,而函数的局部变量地址比 EBP 的值低,因此参数或局部变量总是通过 EBP 加减一定的偏移地址来访问的,比如,要访问函数的第一个参数为 EBP+8。

4) 堆栈中到底存储了什么数据?包括了:函数的参数,函数的局部变量,寄存器的值(用以恢复寄存器),函数的返回地址以及用于结构化异常处理的数据(当函数中有 try…catch 语句时才有,本文不讨论)。这些数据是按照一定的顺序组织在一起的, 我们称之为一个堆栈帧(Stack Frame)。一个堆栈帧对应一次函数的调用。在函数开始时,对应的堆栈帧已经完整地建立了(所有的局部变量在函数帧建立时就已经分配好空间了,而不是随着函数的执行而不断创建和销毁的);在函数退出时,整个函数帧将被销毁。

5) 在文中,我们把函数的调用者称为 caller(调用者),被调用的函数称为callee(被调用者)。之所以引入这个概念,是因为一个函数帧的建立和清理,有些工作是由 Caller 完成的,有些则是由 Callee 完成的。

开始讨论堆栈是如何工作的

我们来讨论堆栈的工作机制。堆栈是用来支持函数的调用和执行的,因此,我们下面将通过一组函数调用的例子来讲解,看下面的代码:

int foo1(int m, int n){    int p=m*n;    return p;}int foo(int a, int b){    int c=a+1;        int d=b+1;        int e=foo1(c,d);        return e;}int main(){    int result=foo(3,4);    return 0;}

这段代码本身并没有实际的意义,我们只是用它来跟踪堆栈。下面的章节我们来跟踪堆栈的建立,堆栈的使用和堆栈的销毁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值