call stack详解

call stack详解callstackxiang:调用堆栈:调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法。栈:在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

call stack详解:
调用堆栈:调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法。
栈:在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶
指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
当发生函数调用的时候,栈空间中存放的数据是这样的:

  1、调用者函数把被调函数所需要的参数按照与被调函数的形参顺序相反的顺序压入栈中,即:从右向左依次把被调函数所需要的参数压入栈;

  2、调用者函数使用call指令调用被调函数,并把call指令的下一条指令的地址当成返回地址压入栈中(这个压栈操作隐含在call指令中);

  3、在被调函数中,被调函数会先保存调用者函数的栈底地址(push ebp),然后再保存调用者函数的栈顶地址,即:当前被调函数的栈底地址(mov ebp,esp);

  4、在被调函数中,从ebp的位置处开始存放被调函数中的局部变量和临时变量,并且这些变量的地址按照定义时的顺序依次减小,即:这些变量的地址是按照栈的延伸方向排列的,先定义的变量先入栈,后定义的变量后入栈;

  所以,发生函数调用时,入栈的顺序为:

  参数N

  参数N-1

  参数N-2

  …..

  参数3

  参数2

  参数1

  函数返回地址

  上一层调用函数的EBP/BP

  局部变量1

  局部变量2

  ….

  局部变量N
解释:

  首 先,将调用者函数的
EBP入栈(push ebp),然后将调用者函数的栈顶指针ESP赋值给被调函数的EBP(作为被调函数的栈底,mov ebp,esp),此时,EBP寄存器处于一个非常重要的位置,该寄存器中存放着一个地址(原EBP入栈后的栈顶),以该地址为基准,向上(栈底方向)能 获取返回地址、参数值,向下(栈顶方向)能获取函数的局部变量值,而该地址处又存放着上一层函数调用时的EBP值;

  一般而言,SS: [ebp+4]处为被调函数的返回地址,SS:[EBP+8]处为传递给被调函数的第一个参数(最后一个入栈的参数,此处假设其占用4字节内存)的 值,SS:[EBP-4]处为被调函数中的第一个局部变量,SS:[EBP]处为上一层EBP值;由于EBP中的地址处总是”上一层函数调用时的EBP 值”,而在每一层函数调用中,都能通过当时的EBP值”向上(栈底方向)能获取返回地址、参数值,向下(栈顶方向)能获取被调函数的局部变量值”;

  如此递归,就形成了函数调用栈;

  函数内局部变量布局示例:

  #include <stdio.h>

  #include <string.h>

  struct C

  {

  int a;

  int b;

  int c;

  };

  int test2(int x, int y, int z)

  {

  printf(“hello,test2\n”);

  return 0;

  }

  int test(int x, int y, int z)

  {

  int a = 1;

  int b = 2;

  int c = 3;

  struct C st;

  printf(“addr x = %u\n”,(unsigned int)(&x));

  printf(“addr y = %u\n”,(unsigned int)(&y));

  printf(“addr z = %u\n”,(unsigned int)(&z));

  printf(“addr a = %u\n”,(unsigned int)(&a));

  printf(“addr b = %u\n”,(unsigned int)(&b));

  printf(“addr c = %u\n”,(unsigned int)(&c));

  printf(“addr st = %u\n”,(unsigned int)(&st));

  printf(“addr st.a = %u\n”,(unsigned int)(&st.a));

  printf(“addr st.b = %u\n”,(unsigned int)(&st.b));

  printf(“addr st.c = %u\n”,(unsigned int)(&st.c));

  return 0;

  }
int main(int argc, char** argv)

  {

  int x = 1;

  int y = 2;

  int z = 3;

  test(x,y,z);

  printf(“x = %d; y = %d; z = %d;\n”, x,y,z);

  memset(&y, 0, 8);

  printf(“x = %d; y = %d; z = %d;\n”, x,y,z);

  return 0;

  }

  打印输出如下:

  addr x = 4288282272

  addr y = 4288282276

  addr z = 4288282280

  addr a = 4288282260

  addr b = 4288282256

  addr c = 4288282252

  addr st = 4288282240

  addr st.a = 4288282240

  addr st.b = 4288282244

  addr st.c = 4288282248

  a = 1; b = 2; c = 3;

  a = 0; b = 0; c = 3;

  
示例效果图:
call stack详解
该图中的局部变量都是在该示例中定义的;
call stack详解

这个图片中反映的是一个典型的函数调用栈的内存布局;

访问函数的局部变量和访问函数参数的
区别:
  
局部变量总是通过
将ebp减去偏移量来访问,函数参数总是通过
将ebp加上偏移量来访问。对于32位变量而言,第一个局部变量位于ebp-4,第二个位于ebp-8,以此类推,32位局部变量在栈中形成一个逆序数组;第一个函数参数位于ebp+8,第二个位于ebp+12,以此类推,32位函数参数在栈中形成一个正序数组。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/223055.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 我的世界怎么显示坐标_我的世界永久显示坐标

    我的世界怎么显示坐标_我的世界永久显示坐标在我的世界游戏中,坐标这个问题如果玩的很6的话能够帮助我们瞬间移动,去到任何想去的地方,这时有不少玩家不禁想问了,我的世界坐标怎么看,坐标指令又是什么呢?坐标(coordinates)在数字上反映了您在主世界中的位置。坐标基于一个由三条交于一点(即原点)的坐标轴而形成的网格。玩家会出生在距离原点数百方块的位置上。x轴反映了玩家距离原点在东()西(-)方向上的距离,如经度。z轴反映了玩家距离原点在…

    2022年4月19日
    1.4K
  • LoadRunner 11 安装及激活成功教程

    LoadRunner 11 安装及激活成功教程注意事项:  安装前,把所有的杀毒软件和防火墙关闭。  若以前安装过LoadRunner,则将其卸载。  安装路径不要带中文字符。  如果系统为WIN7,旗舰版才能安装。  安装完毕,需激活成功教程

    2022年7月22日
    17
  • ccf csp认证真题(护师考试历年真题)

    CSP认证考什么怎么考?CCF计算机职业资格认证的每一道试题都十分经典,覆盖现实世界中方方面面的问题。这个历年试题解主要使用C/C++语言编写,将逐步增加Python和Java的解题程序。程序中附有注释,力求解题思路清晰简洁,值得珍藏与模仿。逐题改写过程中,富文本编辑器写的博客将全部用Markdown编辑器改写。改写的第一题,都增加了Python和Java的解题程序。2021.04第22次:CCF202104-1灰度直方图(100分)【计数】CCF202104-2邻域均值(100分.

    2022年4月17日
    45
  • Java之StringUtils的常用方法

    Java之StringUtils的常用方法StringUtils方法的操作对象是 Java.lang.String类型的对象,是JDK提供的String类型操作方法的补充,并且是null安全的(即如果输入参数String为null则不会抛出NullPointerException,而是做了相应处理,例如,如果输入为null则返回也是null等,具体可以查看源代码)。除了构造器,StringUtils…

    2022年6月12日
    23
  • 计算机操作系统学习笔记 第一章、操作系统概论

    计算机操作系统学习笔记 第一章、操作系统概论详细介绍了计算机系统概论,带大家入门计算机操作系统

    2022年6月28日
    27
  • macbook如何安装双系统_双系统安装方法

    macbook如何安装双系统_双系统安装方法1.官网https://www.microsoft.com/zh-cn/software-download/windows10ISO进入官网下载win10(因为win10仅支持win10专业版不要下家庭中文版)语言:简体中文64位2.用迅雷下否则直接下载时间会很长Mac迅雷-轻体验,大改变(拖动迅雷至文件夹,即可安装,macOS10.15安装后请到应用程序找到图标,右键打开)点击迅雷的加号新建任务在浏览器复制之后自动弹出迅雷下载对话保存位置至桌面点击下载打开浏览器.

    2022年9月28日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号