我正在审查过去的任务,并试图遵循递归函数背后的逻辑。它只是将输入传递给“fact”,确定是否> = 1,并再次将其传递给循环。在循环中,我变得困惑。我知道在调用函数时通常明智的做法是使用sw保存数据并在完成后用lw加载它。但是,循环调用fact(再次调用循环)直到输入< 1.正如您在下面的代码中所看到的,它会不断地将$ ra和输入保存在同一位置。请注意,在实际完成递归之前不会使用lw代码。
这怎么不覆盖旧数据?旧数据仅仅被推迟了吗?如果是这样,当递归多次使用时,如何只弹出两个项目?这段代码中$ v0的目的是什么?
fact: slti $t0, $a0, 1 # test for n < 1, n is user input
beq $t0, $zero, L1 # if n >= 1, go to L1
li $v0, 1 # return 1
jr $ra # return to instruction after jal
L1: addi $sp, $sp, -8 # adjust stack for 2 items
sw $ra, 4($sp) # save the return address
sw $a0, 0($sp) # save the argument n
addi $a0, $a0, -1 # n >= 1; argument gets (n – 1)
jal fact # call fact with (n – 1)
lw $a0, 0($sp) # return from jal: restore argument n
lw $ra, 4($sp) # restore the return address
addi $sp, $sp, 8 # adjust stack pointer to pop 2 items
mul $v0, $a0, $v0 # return n * fact (n – 1)
jr $ra # return to the caller
答案 0 :(得分:0)
这是堆栈操作的典型示例。
每次执行该函数时,它都会增加堆栈并将$ra
和$a0
存储在新分配的位置。
您可以通过注意$ra
和$a0
相对于$sp
(堆栈指针)进行分配来看到这一点。
每次函数执行时,通过从8
减去$sp
或者为两个单词减去足够的空间来扩展堆栈。同样,当函数退出堆栈时,将8
添加到$sp
即可缩小。
这样想:
假设我们要计算5!。
每次拨打fact
时的筹码都会存储$a0
和$ra
。在对fact
进行5次调用后,堆栈将如下所示:
$a0: 5
$ra: back to main
----
$a0: 4
$ra: back to fact
----
$a0: 3
$ra: back to fact
----
$a0: 2
$ra: back to fact
----
$a0: 1
$ra: back to fact
----
一旦基本案例执行($a0
= 1),堆栈就会开始缩小。 $a0
= 1堆栈在1
中返回$v0
,当我们加载$a0
时,我们得到2
,因为我们已经缩小了堆栈。因此,我们将2
乘以1
,然后我们返回该值。在下一个堆栈帧中重复相同的过程,我们在2
中返回$v0
并将其乘以我们从堆栈加载的3
并返回6
在$v0
。
希望从这里你可以看到:
addi $a0 $zero 5
jal fact
会在120
中返回$v0
。