所以我有这个代码 计算阶乘(一般) 但在这个例子中,它计算的阶乘为10
.data 0x10008000
.word 10
.word 1
.word 0
.ascii "The factorial of 10 is %d \n"
.text
.globl main
main:
addi $sp, $sp, -32
sw $ra, 20($sp)
sw $fp, 16($sp)
addiu $fp, $sp,28
lw $a0, 0($gp)
jal fact
...
lw $ra, 20($sp)
lw $fp, 16($sp)
addiu $sp, $sp, 32
jr $ra
fact:
addiu $sp, $sp, -32
sw $ra, 20($sp)
sw $fp, 16($sp)
addiu $fp, $sp, 28
sw $a0, 0($fp)
lw $v0, 0($fp)
lw $t0, 4($gp)
slt $t1,$v0,$t0
bne $t0, $t1, L2
addi $v0, $v0, 1
jr L1
L2:
lw $v1, 0($fp)
addi $v0, $v1, -1
sw $v0, 8($gp)
lw $a0, 8($gp)
jal fact
lw $v1, 0($fp)
mul $v0, $v0, $v1
L1:
lw $ra, 20($sp)
lw $fp, 16($sp)
addiu $sp, $sp, 32
jr $ra
我的问题是这个 不需要一个 jr L1 L2中乘法后的命令? 递归是如何工作的? 是否需要某种方式来存储以前的数字? 我认为这是堆栈的工作,但在我看来每次都是这样 该函数被称为先前存储的变量ars覆盖。
ps我不知道是否有人理解我的问题 对不起我的英语很差......
答案 0 :(得分:2)
fact
函数的工作方式如下:
int fact(unsigned int n) {
if (n < 1) {
return n+1;
} else {
return fact(n-1) * n;
}
}
正如你所看到的,它递归调用自己直到参数变为< 1
,此时它返回1
(即0+1
)并返回调用链以执行乘法运算
例如,如果您执行fact(3);
,则调用链将如下所示:
fact(3): fact(2) * 3
fact(2): fact(1) * 2
fact(1): fact(0) * 1
fact(0): 1
1
* 1 (==1)
* 2 (==2)
* 3 (==6)
函数的值在$v0
中返回。要获取n
的当前值以执行乘法fact(n-1) * n
,函数会将其存储在当前堆栈帧(sw $a0, 0($fp)
)上并在乘法之前将其读回({{1} })。
正如Michael Burr评论的那样,在lw $v1, 0($fp)
的每个条目上都会创建一个新的堆栈帧。这就是fact
的前4个指令所做的事情(在堆栈上保留一些空间,保存当前帧指针和返回地址,并将帧指针指向新的堆栈帧)。
在乘法之后没有必要跳转到fact
,因为L1
紧随其后(即无论如何都会到达标签)。
答案 1 :(得分:0)
递归需要引入“手动堆栈管理”,这显然需要付出一些努力才能实现。 并非所有编译器都出于各种原因支持它
这一切都发生在堆栈上,它在不同的参数树之间扩展和收缩,是一件非常令人惊奇的事情。
如果你想“了解那些东西”,我建议你对“河内塔”做一些研究,这是一个高度递归的现实问题
这里有一个很好的解释
http://www.cs.cmu.edu/~cburch/survey/recurse/hanoiimpl.html
一旦理解了递归,它就可以用于非常复杂的问题,比如数独求解器
典型的数独游戏有大约20到30种不同的场景
因此,可以通过生成单个递归解决方案来规避20多个手动场景的编程,这个解决方案也可以为“不可能的”sudokos神奇地工作