C++函数调用过程中栈区的变化

先简单说一下什么是栈帧

大多数机器的数据传递、局部变量的分配和释放通过操纵程序栈来实现。为单个过程(函数调用)分配的那部分栈称为栈帧。

栈帧 stack frame

定义:机器用栈来自传递过程参数,存储返回信息,保存寄存器为以后恢复及本地存储。

作用:用于控制和保存一个函数调用过程的所有信息的

组成:由两个寄存器组成的 -- 栈指针和帧指针。

  • 栈指针%esp是指向栈帧的顶部,是可以移动的。%esp指向低地址
  • 帧指针%ebp是指向栈帧的最底部,一般用他来访问栈里的元素。只要栈帧没有消失,帧指针是不会移动的。%ebp指向高地址

注意:栈的地址是从高地址往低地址延伸,函数每一次的调用都有自己独立的栈帧。

 

栈帧示意图

一般来说,我们将 %esp 到 %ebp 之间区域当做栈帧。并不是整个栈空间只有一个栈帧,每调用一个函数,就会生成一个新的栈帧。

在函数调用过程中,我们需要用栈保存以下数据:

  • 调用函数需要知道在哪里获取被调用函数返回的值;
  • 被调用函数需要知道传入的参数在哪里,返回的地址在哪里。
  • 需要保证在被调用函数返回后,%ebp%esp 等寄存器的值应该和调用前一致。

可以去看看函数的调用示例的汇编代码,这样会更快理解,这里有一些例子

  • http://www.cs.virginia.edu/~evans/cs216/guides/x86.html
  • https://www.cnblogs.com/clover-toeic/p/3755401.html

函数调用流程

简单的来说就是先将函数压栈,当函数调用结束后,再出栈

普通函数调用流程:

  1. 开辟栈帧空间
  2. 函数参数从右至左进行压栈
  3. 函数返回地址进行压栈
  4. 函数局部变量进行压栈

虚函数调用流程(大体)

  1. 查找this指针(也就是实例)的地址
  2. 根据this指针,查找虚函数表(函数指针数组)的地址
  3. 从虚函数表中,取出相应的函数地址
  4. 之后的调用和普通函数调用方式一致

为什么参数从右至左压栈

  1. 可以动态变化参数个数。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。这样的话,除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。
  2. 更符合习惯 采用这种顺序,是为了让程序员在使用C/C++的“函数参数长度可变”这个特性时更方便。
    • 什么是“函数参数长度可变”?printf就是一个例子,它的参数的个数就是可变的。比如,printf"%d %d %d",1,2,3),在采用从右向左的参数入栈顺序时,参数出栈顺序时"%d %d %d",1,2,3如果采用从左向右的入栈顺序,则出栈顺序变为3,2,1,"%d %d %d",这样就不能提前知道会有多少个参数

 

 Reference:

  1. https://www.cnblogs.com/sddai/p/9762968.html
  2. https://www.cnblogs.com/wiesslibrary/p/15727311.html
posted @ 2022-03-22 07:39  cancantrbl  阅读(557)  评论(0编辑  收藏  举报