对于栈内存的理解

1、栈

堆栈非常重要,因为它跟踪程序中运行的函数,而函数又是软件的基本组成模块。当调用一个函数时,将创建一个栈桢来支持该函数的执行。栈桢包含函数的局部变量和调用者传递给函数的参数。栈桢还包含管理信息,允许被调用的函数(被调用方)安全地返回给调用方。(saved ebp、 return address)

2、与栈相关的寄存器

  • 堆栈指针(esp)指向堆栈的顶部。顶部总是被最后一个被压入到堆栈上但尚未弹出的项目所占据,就像在现实世界中的一堆盘子。存储在esp中的地址随着栈的内容的压入和弹出而不断变化,因此它总是指向最后一项
  • 跟踪堆栈的第二个寄存器是ebp,即基指针或帧指针。它指向当前运行的函数的栈桢中的一个固定位置,并为访问函数参数和本地变量提供一个稳定的参考点(基)。ebp仅在函数调用开始或结束时更改。因此,我们可以很容易地将堆栈中的每个项作为ebp的偏移量来处理。与esp不同,ebp主要由程序代码维护,很少受到CPU干扰。有时候,完全抛弃ebp会带来性能上的好处,这可以通过编译器标志来实现。Linux内核就是这样做的一个例子。
  • eax寄存器用于将大多数C数据类型的返回值传输回调用方。
  • pc寄存器用来确定cpu下一条执行的指令

3、一个简单函数的栈内存的管理

int add(int a, int b)
{
        int result = a + b;
        return result;
}
int main(int argc)
{
        int answer;
        answer = add(40, 2);
}
(1)该函数栈内存的变化时序图

在这里插入图片描述当main发起函数调用(调用add函数)时:

  • 首先要将函数的返回地址保存下来(pc 下一条执行指令的地址)
  • 其次,要将上一个栈帧的的栈底地址保存起来(即main函数栈帧的栈底地址)。

当函数返回时,该栈的内容将会全部弹出,然后将函数的返回值保存在eax寄存器中。

上述函数弹出的具体顺序是

  • result 保存在eax后,弹出。此时esp指向saved ebp。
  • saved ebp 的内容存到复制到ebp寄存器中(即这时ebp返回指向到main的栈底),然后saved ebp这块内存弹出。此时esp指向return address 这块内存中。
  • return address通过一个操作系统指令(ret指令),将其内容拷贝到pc寄存器中,然后这块内容弹出。这时,pc寄存器指向调用函数后面的语句。而此时esp则指向了a。

这样就完成了函数的返回。add函数的栈帧完全消失。

从上述图片可以看出:

  • 调用函数add时,add栈内部中,并不保存调用add所传递过来的参数,因为这两个参数保存在寄存器中。add函数是可以访问到这两个参数的,所以也可以认为它被保存到栈中了。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值