请求协助将C代码转换为MIPS汇编,当前实现陷入死循环
问题:将C递归函数转换为MIPS汇编时遇到死循环
我需要把下面的C代码转换成MIPS汇编,但自己写的代码陷入死循环。我知道死循环的原因,但想不出其他可行的实现方式(假设$a0存储数组地址,$v0为返回寄存器)。
C代码
uint32_t reverse_sum(uint32_t *arr) { uint32_t r; if(*arr == -1) return 0; r = reverse_sum(arr+1) + (uint32_t)*arr; *arr = r; return(r); }
我尝试编写的MIPS代码
reverse_prefix_sum: # PROLOGUE subu $sp, $sp, 8 # expand stack by 8 bytes sw $ra, 8($sp) # push $ra (ret addr, 4 bytes) sw $fp, 4($sp) # push $fp (4 bytes) addu $fp, $sp, 8 # set $fp to saved $ra lw $t0, 0($a0) # t0 now stores first array element bne $t0, -1, rec li $v0, 0 j ret rec: subu $sp, $sp, 4 # expand stack by 1 spot sw $t0, 4($sp) # store t0 (first element in array) on the stack lw $a0, 4($a0) # load arr + 1 element in a0 jal reverse_prefix_sum lw $a0, 4($sp) add $t1, $v0, $a0 sw $t1, 0($t0) move $v0, $t1 ret: # EPILOGUE move $sp, $fp # restore $sp lw $ra, ($fp) # restore saved $ra lw $fp, -4($sp) # restore saved $fp jr $ra # return to kernel
问题分析与修正方案
你的代码存在的核心错误
- 递归参数传递错误:
lw $a0, 4($a0)是读取arr[1]的值,而非传递arr+1的地址,导致递归调用时传入的不是下一个数组元素的地址,而是元素值,后续递归逻辑完全混乱,必然引发死循环或内存访问错误。 - 栈操作与地址恢复错误:递归返回后,你从栈中取出的是原
arr[0]的值而非原数组地址,导致sw $t1, 0($t0)是将结果写入以原元素值为地址的内存区域,这会破坏内存,进一步加剧错误。 - 栈帧维护错误:尾声部分恢复
$fp的操作lw $fp, -4($sp)逻辑错误,会导致栈帧混乱,返回时出错。
正确的MIPS实现
reverse_sum: # 栈帧初始化:保存$ra、$fp,以及需要保留的寄存器/变量 subu $sp, $sp, 16 # 栈帧大小16字节:$ra(4)、$fp(4)、原$a0(4)、原arr[0]值(4) sw $ra, 12($sp) # 保存返回地址到栈 sw $fp, 8($sp) # 保存帧指针到栈 addu $fp, $sp, 16 # 设置帧指针指向栈帧顶部 sw $a0, 4($sp) # 保存当前数组地址到栈,递归返回后需要用它修改数组元素 lw $t0, 0($a0) # 读取当前数组元素值到$t0 # 终止条件判断:如果当前元素是-1(即0xFFFFFFFF,因为uint32_t),返回0 li $t1, 0xFFFFFFFF beq $t0, $t1, base_case # 递归调用:传入arr+1的地址 addu $a0, $a0, 4 # 计算arr+1的地址(uint32_t占4字节) jal reverse_sum # 递归返回后计算r = 返回值 + 原arr[0]值 lw $a0, 4($sp) # 恢复原数组地址 lw $t0, 0($a0) # 再次读取原arr[0]值(也可以提前保存到栈) add $v0, $v0, $t0 # $v0 = reverse_sum(arr+1) + *arr # 修改当前数组元素为r sw $v0, 0($a0) j epilogue base_case: li $v0, 0 # 终止条件返回0 epilogue: # 恢复栈帧与寄存器 lw $ra, 12($sp) # 恢复返回地址 lw $fp, 8($sp) # 恢复帧指针 addu $sp, $sp, 16 # 收缩栈 jr $ra # 返回
代码说明
- 栈帧管理:提前分配足够的栈空间,保存当前数组地址和元素值,确保递归返回后能正确恢复上下文。
- 递归参数传递:通过
addu $a0, $a0, 4计算下一个数组元素的地址,而非读取元素值,保证递归调用的参数正确。 - 终止条件判断:因为
uint32_t的-1实际是0xFFFFFFFF,所以用li $t1, 0xFFFFFFFF来比较,避免符号扩展问题。 - 结果写入:递归返回后,用保存的原数组地址修改当前元素,确保写入的是正确的内存位置。
内容的提问来源于stack exchange,提问作者pwlhuqn
相关产品推荐
相关产品推荐

