我得到了下面的代码示例。
代码从 $a0
中的任何数字(作为参数传递)开始计数/打印到 0。除了使用 $ra
所做的事情之外,我可以跟踪所有事情。那部分让我感到困惑。
我知道当我们使用 jal
时,$ra
会跟踪返回地址(因此程序知道要返回到哪里)。跟踪代码,首先 $ra
保存在堆栈上,并设置为函数执行完毕后我们将返回的位置。但是,每次再次调用 printnums
时,这不是会创建一个新的 $ra
值吗?然后该值将保存在内存中。这意味着当我们最后 jr
时,它将跳转到保存的 $ra
的最后一个值(它指向 ret:
在jal printnums
之后)?
或者是每次保存 $ra
时都会在内存中保留 4 个额外字节,而不是一遍又一遍地覆盖相同的 4 个字节?如果是这种情况,当我们释放堆栈上的空间时,我们会“弹出”每个 $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
最佳答案
这是一个递归函数。在函数的每次迭代期间,堆栈都会增加 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
的调用者从而按预期运行。
关于MIPS——追踪这段代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24026888/