在递归函数中执行 JAL 之前 $ra 中存储了什么
What is stored in $ra before we perform JAL in a recursive function
在这个函数的第 1 次迭代中,我们可以执行 recur:
并执行此行:sw $ra, 0($sp)
,它在任何 jal
语句之前完成。据我了解,jal
(跳转和link)语句将输入到$ra
寄存器,直接在jal
之后的位置。那么,如果 $ra
中还没有存储任何内容,示例中会发生什么?
这是一个函数;在条目 $ra
上保存着 return 您的呼叫者希望您跳回的地址。
除了在 $a0..$a3 中包含前 4 个整数参数外,这是调用者和被调用者之间达成一致以使函数调用正常工作所必需的契约的一部分。 调用约定 的这一部分当然是围绕 MIPS jal
指令设计的,因此功能 call/return 可以每个指令完成。这也是 </code> 具有符号名称 <code>$ra
= return 地址的原因。
寄存器不能容纳“空”,它始终是 32 位值。
如果某些代码将 $ra
设置为 0 或某个无效地址并使用 j
而不是 jal
跳转到此函数,它会在 returned 时崩溃那将是来电者的错。您的函数可以简单地假设 $ra
在函数入口处持有一个有效的 return 地址,无论是来自递归调用还是来自其他调用者。
(这是递归函数的一点,您实际上只是对该函数进行函数调用,而您从哪里调用并不重要。)
请注意,程序中的顶级标签(例如在 MARS 模拟器中)不是严格意义上的函数。
在 MARS 中,通常将此标签称为 main
,尽管事实上 $ra
持有垃圾或 0
在这一点上,因此它必须通过 exit 系统调用退出; jr $ra
会崩溃。
(在 C 中,main
是一个真正的函数,通常从 _start
入口点到达,该入口点在调用 main 之前设置内容。在 C 中,main
本身可以递归而无需任何特殊技巧。如果您在真正的 OS 下编写程序 运行,而不是 MARS 模拟的玩具系统,您通常使用 _start:
标签作为内核的真实入口点,或者你只需编写一个 main 和 link 以及调用 main
.)
的 C 库提供的启动代码
在这个函数的第 1 次迭代中,我们可以执行 recur:
并执行此行:sw $ra, 0($sp)
,它在任何 jal
语句之前完成。据我了解,jal
(跳转和link)语句将输入到$ra
寄存器,直接在jal
之后的位置。那么,如果 $ra
中还没有存储任何内容,示例中会发生什么?
这是一个函数;在条目 $ra
上保存着 return 您的呼叫者希望您跳回的地址。
除了在 $a0..$a3 中包含前 4 个整数参数外,这是调用者和被调用者之间达成一致以使函数调用正常工作所必需的契约的一部分。 调用约定 的这一部分当然是围绕 MIPS jal
指令设计的,因此功能 call/return 可以每个指令完成。这也是 </code> 具有符号名称 <code>$ra
= return 地址的原因。
寄存器不能容纳“空”,它始终是 32 位值。
如果某些代码将 $ra
设置为 0 或某个无效地址并使用 j
而不是 jal
跳转到此函数,它会在 returned 时崩溃那将是来电者的错。您的函数可以简单地假设 $ra
在函数入口处持有一个有效的 return 地址,无论是来自递归调用还是来自其他调用者。
(这是递归函数的一点,您实际上只是对该函数进行函数调用,而您从哪里调用并不重要。)
请注意,程序中的顶级标签(例如在 MARS 模拟器中)不是严格意义上的函数。
在 MARS 中,通常将此标签称为 main
,尽管事实上 $ra
持有垃圾或 0
在这一点上,因此它必须通过 exit 系统调用退出; jr $ra
会崩溃。
(在 C 中,main
是一个真正的函数,通常从 _start
入口点到达,该入口点在调用 main 之前设置内容。在 C 中,main
本身可以递归而无需任何特殊技巧。如果您在真正的 OS 下编写程序 运行,而不是 MARS 模拟的玩具系统,您通常使用 _start:
标签作为内核的真实入口点,或者你只需编写一个 main 和 link 以及调用 main
.)