带你学习《深入理解计算机系统》程序语言的底层描述(4)——递归函数汇编栈帧实现

本文深入解析了递归函数的栈帧实现,通过讲解`mov`和`lea`指令的用法,帮助读者理解汇编语言中数据传送和地址计算的过程。接着,以`fib_rec`递归函数为例,详细剖析了递归调用的栈帧结构和内存布局,展示了如何在汇编层面实现递归。通过对函数的分析,揭示了递归调用中参数保存、返回值传递和栈空间管理的细节。
摘要由CSDN通过智能技术生成

这一节我们来讨论递归函数栈帧实现。

 

一:mov和lea的含义和用法

在论述递归栈帧之前,回到第一节(1)中关于mov的阐述,说实话,刚开始写这些文字的时候,我以为自己理解了mov的含义,但是当看到lea操作时,大脑却瞬间混乱,连之前认为熟练的mov也忽然打不到方向了。因此这里有必要再次详细阐述下mov和lea的实际含义,以免在后面的讨论中混淆。

先回忆下mov(数据传送指令),我提到过有两种用法,一种是值传递:mov  $4,  %eax和 mov  %ebx,%eax。 都是把数值4或者寄存器ebx里的数值传送到寄存器eax;

第二种用法是间接引用,如mov 4(%ebx), %eax,就是用诸如"()"的方式把寄存器里的数值当成地址理解,并根据这个地址寻找到存储器的相应位置,并读出值,再传送给%eax。

关于间接引用,汇编和C语言在概念上完全一致,只是在实现方式上有所不同。我们熟悉的C语言在定义变量时,把存储地址的变量和存储数值的变量区分开来,于是才有无符整型变量和无符整型指针变量u_int a与u_int *a的区别,事实上在32位系统中他们都是占领4字节空间,并且存储的都是32位无符整数,只是在一些算术语句和数组运算以及“*”等操作时能体现不同的处理策略。而在汇编语言中,寄存器就是寄存器,不存在指针寄存器或整数寄存器的区别(之前的栈指针%esp和帧指针%ebp只是编译器设计时为了便于理解而约定俗成的名称指定)。寄存器里面可以存储数值,至于这个数值是地址还是其他,对CPU来说并不重要,只是当编译器觉得他存的是地址并希望找到相应的存储位置时,就可以利用“()”操作符,像C语言中的“*”那样,进行间接寻址罢了。C语言之所要定义出指针变量,一是为了使得间接寻址更直观好理解,二是特定类型按约定长度跳步更方便(和数组类似)。但即便这样,埋怨C指针和数组概念抽象的小伙伴仍是人山人海……

清楚了间接寻址,我们再来看lea(加载有效地址Load Effective Address),好高大上的名字,咋一看感觉这又是要把间接寻址玩翻天的操作,其实没那么麻烦。

看看上面所说的mov如何处理间接寻址实现数据传送的?分为三步:

1、计算地址值,要么直接给常数,要么直接是寄存器里的值,要么对寄存器里的值进行一系列运算得出的值;

2、根据得到的地址值引用其空间中的数值;

3、将得到的数值传送给接收方(寄存器)

 

上面说的是mov,那lea是想干什么伟大工程呢?其实它就是省略第2步操作,直接从1跳到3,解释完毕!举例如下:

假设%ebx的值是0x108,而0x108是某个存储器地址,里面存数值0xCD,那么如下两条语句:

movl  (%ebx), %eax //1、从ebx中读出地址值0x108;2、根据地址0x108间接寻址到存储器,并将其中的0xCD挖出来;3、将0xCD传送给eax

leal   (%ebx), %eax //1、从ebx中读出地址值0x108;2、对不起,作为lea我不干这一步嚯嚯呵呵哈哈…………   ;3、将0x108传送给eax

 

简单吧?如果说mov可以间接寻址引用,那lea无非就是寻址,但不引用。其实另一种理解方式是,lea中的"

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值