我得到了下面的代码示例。
代码从$a0
(作为参数传递)中的任何数字计数/打印到0。除了$ra
所做的事情之外,我可以关注所有事情。那部分令我困惑。
我知道我们何时使用jal
$ra
跟踪返回地址(因此程序知道返回的位置)。尽管在$ra
处追踪代码仍保存在堆栈中,并设置为在函数执行完毕后返回的位置。但是,每次再次调用printnums
时,都不会创建新的$ra
值?然后该值将保存在内存中。这意味着当我们jr
结束时,它会跳转到保存的$ra
的最后一个值(指向ret:
之后的jal printnums
)?
或者每次保存$ra
时,是否在内存中保留了4个额外字节而不是反复覆盖相同的4个字节?如果是这种情况,那么当我们在堆栈上释放空间时,我们就会“弹出”#39;每个$ra
,直到我们点击保存的第一个并返回到函数的正确位置。那是对的吗?
我只是想确保我正确理解这一点。谢谢您的帮助。
printnums: # $a0 has value
addi $sp, $sp, -4 # reserve room for return address
sw $ra, ($sp) # save return address
beq $a0, $0, ret # check ending condition
addi $v0, $0, 1 # set command to print int
syscall # print value in $a0
addi $a0, $a0, -1 # decrement value
jal printnums # call printnums again
ret:
lw $ra, ($sp) # restore $ra
addi $sp, $sp, 4 # deallocate space on stack
jr $ra # return
答案 0 :(得分:3)
这是一个递归函数。在函数的每次迭代期间,堆栈增加4个字节以保存返回地址,并打印一个数字。
想象一下,我们有这段代码调用printnums:
caller:
li $a0 4
jal printnums
这是每次调用后堆栈的样子(都有$a0
的不同值)
╔════════╦════════╦════════╦════════╗
$a0:║ 4 ║ 3 ║ 2 ║ 1 ║
╠════════╬════════╬════════╬════════╣
stack:║ caller ║ caller ║ caller ║ caller ║
║ ║ ret ║ ret ║ ret ║
║ ║ ║ ret ║ ret ║
║ ║ ║ ║ ret ║
╚════════╩════════╩════════╩════════╝
当$a0
达到值0时,堆栈开始展开,每次printnums
的调用开始返回时,每个值都会逐个弹出。最近的四次调用将从堆栈中恢复$ra
并返回ret
,但最后一次将弹出返回值caller
并返回printnums
的调用者按预期运作。