计算机系统——菲波拉契函数执行过程中栈帧变化情况

前言

本文内容是深入汇编层次感受递归函数栈帧变化,题目来源是hnu计算机系统讨论课。花了大半天的时间,但是觉得很值得,对程序的机器级表示又有了更深的理解。



提示:以下是本篇文章正文内容,下面案例可供参考
题目:

小学生小军在学习递归的概念后,很轻松的写出了求菲波拉契数列的c语言代码:

#include"stdio.h"

int f(int n)

{

  if (n<=0) return 0;

  if (n==1 || n==2) return 1;

  return f(n-1)+f(n-2);

}

int main()

{

  int i=f(5);

  printf("%d\n",i);

  return 0;

}

在得知某些计算机专业的大学生要很费劲才能写出这个程序后,他很骄傲自满,为了让小军明白计算机专业知识的博大精深,我们决定向小军演示整个函数执行过程中栈帧变化情况

要求:

从进入main函数开始到结束,每一次函数调用都至少有一个栈帧的示意图,并标注栈顶、栈底,局部变量,参数,旧ebp,返回地址等内容。


分析:

1.斐波那契数列1-5分别为:1 1 2 3 5

2.函数调用顺序

在这里插入图片描述

在这里插入图片描述

一.main函数

gdb -q fib
b main
r
diassem
ni
si

在这里插入图片描述

main函数栈帧准备(

开辟栈空间,5是待传递的参数,放到栈顶

将返回地址放到栈顶,esp下移4个字节
在这里插入图片描述

地址内存空间标记
0xbffff138old ebp%ebp
0xbffff1105
0xbffff10c0x0804847f%esp
在这里插入图片描述

二.f函数

此时进入f函数
在这里插入图片描述
在这里插入图片描述

2.进入f(5) f(5)->f(4)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)%ebp
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)%esp
在这里插入图片描述

3. 进入f(4) f(4)->f(3)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)%ebp
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)%esp
在这里插入图片描述

4. 进入f(3) f(3)->f(2)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0bc2
0xbffff0ac0x08048452(返回地址)%esp
在这里插入图片描述

5-1.f(2)-前面的判断部分

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0bc2
0xbffff0ac0x08048452(返回地址)
0xbffff0a80xbffff0c8(old ebp)%ebp
0xbffff0a40xb7fc0000(ebx)
0xbffff0901%esp

5-2.f(2)中-add esp

在这里插入图片描述

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0bc2
0xbffff0ac0x08048452(返回地址)
0xbffff0a80xbffff0c8(old ebp)%ebp
0xbffff0a40xb7fc0000(ebx)%esp
0xbffff0901
在这里插入图片描述

5-3.f(2)中-pop %ebx

在这里插入图片描述

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0bc2
0xbffff0ac0x08048452(返回地址)
0xbffff0a80xbffff0c8(old ebp)%ebp %esp
0xbffff0901
在这里插入图片描述

5-4.f(2)中-pop %ebp

在这里插入图片描述

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0bc2
0xbffff0ac0x08048452(返回地址)%esp
0xbffff0901
在这里插入图片描述

6.返回f(3)

在这里插入图片描述

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0b02%esp
0xbffff0ac0x08048452(返回地址)
0xbffff0901
在这里插入图片描述

在这里插入图片描述

7.返回f(3)->f(1)

在这里插入图片描述

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)%esp
0xbffff0901
在这里插入图片描述

11.返回f(3) f(2)+f(1)

a

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)%esp
0xbffff0901

b

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01%esp
0xbffff0ac0x08048462(返回地址)
0xbffff0901

12.返回f(3) f(2)+f(1)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)%esp
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

在这里插入图片描述

13.返回f(3) f(2)+f(1)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)%ebp
0xbffff0e40xb7fc0000(ebx)
0xbffff0d03
0xbffff0cc0x08048452(返回地址)%esp
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901
在这里插入图片描述

14.返回f(4) f(4)->f(2)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f04
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)%ebp
0xbffff0e40xb7fc0000(ebx)
0xbffff0d02
0xbffff0cc0x08048462(返回地址)%esp*
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

15.返回f(5) f(5)->f(3)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)%ebp
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)%esp
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d01
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

16.进入f(3) f(3)->f(2)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)&ebp
0xbffff0e40xb7fc0000(ebx)
0xbffff0d02
0xbffff0cc0x08048452(返回地址)%esb
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

17.进入f(2)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d02
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)%ebp
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01%esp
0xbffff0ac0x08048462(返回地址)
0xbffff0901

18.返回f(3) f(3)->f(1)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)%ebp
0xbffff0e40xb7fc0000(ebx)
0xbffff0d01
0xbffff0cc0x08048452(返回地址)%esp
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

19.返回f(5)

地址内存空间标记
0xbffff138old ebp
0xbffff1105
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)%ebp
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)%esp
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d01
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

20.返回main

在这里插入图片描述

地址内存空间标记
0xbffff138old ebp%ebp
0xbffff12c5
0xbffff1145
0xbffff1100x8048530%esp
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d01
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

21.调用输出函数

地址内存空间标记
0xb7c000%ebp
0xbffff138old ebp
0xbffff12c5
0xbffff1145
0xbffff1100x8048530
0xbffff10c0x0804847f(返回地址)%esp
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d01
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901
在这里插入图片描述

22.结束

地址内存空间标记
0xb7c000
0xbffff138old ebp%ebp
0xbffff12c5
0xbffff1145
0xbffff1100x08048530%esp
0xbffff10c0x0804847f(返回地址)
0xbffff1080xbffff138(old ebp)
0xbffff1040xb7fc0000(ebx)
0xbffff0f03
0xbffff0ec0x08048452(返回地址)
0xbffff0e80xbffff108(old ebp)
0xbffff0e40xb7fc0000(ebx)
0xbffff0d01
0xbffff0cc0x08048452(返回地址)
0xbffff0c80xbffff0e8(old ebp)
0xbffff0c40xb7fc0000(ebx)
0xbffff0b01
0xbffff0ac0x08048462(返回地址)
0xbffff0901

尾言

最大的发现是f(3)调用 f(2)产生的数据在f(3)调用f(1)是会被覆盖掉,而不是在其他的内存地址去开辟栈空间。f(2)的返回值被保存在ebx中,供f(3)使用(return f(2)+f(1)),其他f调用同理。故ebx是非常重要的!压栈千万不要忽略它呀!

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法generate()是C++ STL的一个函数,它用于生成容器的元素。它的语法如下: ```c++ template <class ForwardIterator, class Generator> void generate (ForwardIterator first, ForwardIterator last, Generator gen); ``` 其,第一个参数是容器的起始迭代器,第二个参数是容器的结束迭代器,第三个参数是一个生成器函数,用于生成容器的元素。 下面分别介绍如何在容器生成随机数和生成波拉数列。 1. 生成随机数 可以使用C++标准库的rand()函数来生成随机数。由于rand()函数生成的随机数是一个伪随机数,因此在使用前需要先调用srand()函数来进行初始化。 下面是一个使用generate()函数生成随机数的示例代码: ```c++ #include <iostream> #include <vector> #include <cstdlib> #include <ctime> #include <algorithm> using namespace std; int main() { srand(time(NULL)); // 初始化随机数生成器 vector<int> v(10); // 容器大小为10 generate(v.begin(), v.end(), rand); // 生成随机数 for (auto i : v) { cout << i << " "; } cout << endl; return 0; } ``` 输出结果: ``` 1804289383 846930886 1681692777 1714636915 1957747793 424238335 719885386 1649760492 596516649 1189641421 ``` 2. 生成波拉数列 波拉数列是指第一项和第二项均为1,从第三项开始,每一项均为前两项之和的数列。可以使用一个lambda表达式作为生成器函数,生成波拉数列。 下面是一个使用generate()函数生成波拉数列的示例代码: ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v(10); // 容器大小为10 int a = 1, b = 1; generate(v.begin(), v.end(), [&a, &b]() { int c = a + b; a = b; b = c; return a; }); // 生成波拉数列 for (auto i : v) { cout << i << " "; } cout << endl; return 0; } ``` 输出结果: ``` 1 2 3 5 8 13 21 34 55 89 ``` 注意,在lambda表达式,我们使用了a和b的引用,因为它们需要在生成器函数保持状态。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值