基于递归的C转MIPS十进制转二进制转换器代码问题咨询
问题:MIPS递归十进制转二进制代码的错误排查与实现指导
你提供的C递归代码(先帮你补全小问题:d==0时应该return 0,否则无返回值会导致运行异常):
#include <stdio.h> int d2b(int d) { if(d == 0) { return 0; // 原代码无返回值,此处补全 } else { return (d %2 + 10 * d2b(d/2)); } } int main() { int d = 99; int b; b = d2b(d); printf("Input => %d \n", d); printf("Output => %d ", b); return 0; }
你写的初步MIPS代码:
.data msg1 .asciiz “Number is “ msg2 .asciiz “\nConverted to \n“ .text .globl main main: li $v0, 4 la $a0, msg1 syscall li $v0, 5 #Exit syscall 这里注释错误,5是读整数指令 syscall add $a0, $v0, $zero jal fact add $a0, $v0, $zero li $v0, 1 syscall li $v0, 10 la $a0, msg2 syscall fact: li $t0 0 #load 0 beq $a0, $t0, skip #test n li $v0 0 jr $ra skip: subu $sp, $sp, 32 sw $ra 20($sp) sw $fp, 16($sp) addiu $fp, $sp, 28 sw $a0, 0($fp) #save n li $t1 2 #load 2 divu $a0 $t1 #n / 2 mfhi $t2 #remainder mflo $t3 #quotient move $a0, $t3 #n = quotient addi $v0, $a1, 10 jal fact lw $a0, 0($fp) #restore n multu $v0, $a0 lw $ra, 20($sp) lw $fp, 16($sp) addiu $sp, $sp, 32 jr $ra
我帮你拆解核心问题,再讲清楚实现逻辑
一、Syscall的使用错误与正确用法
首先得明确:每个syscall功能对应$v0里的固定数值,调用时要先给$v0赋值,再传参数到指定寄存器,最后执行syscall。你的代码里有这些错误:
- 注释错误:
li $v0,5的注释写了“Exit syscall”,但5是读取整数的syscall,退出程序对应的是$v0=10 - 执行顺序错误:你先调用了
li $v0,10(退出),再调用la $a0,msg2和syscall,此时程序已经退出,msg2根本不会输出 - 字符串引号错误:
.asciiz后面用了中文引号“,MIPS只识别英文双引号",会导致编译报错 - 输入处理缺失:读取整数后,你没有将输入值保存到其他寄存器,后续如果要打印输入值会丢失数据
常用的几个syscall你要记牢:
$v0=1:输出整数(参数放在$a0)$v0=4:输出字符串(参数放在$a0,用la加载字符串地址)$v0=5:读取整数(结果存在$v0)$v0=10:退出程序
二、递归函数的逻辑错误
你的递归函数名字写成了fact(阶乘),但实际要实现的是d2b,而且逻辑完全颠倒:
- 终止条件搞反:C代码里
d==0时返回0,但你的MIPS代码中,当$a0==0时跳去skip执行递归,反而直接jr $ra返回0,逻辑完全错误 - 栈帧操作时机错误:应该先建立栈帧(分配栈空间、保存
$ra和$fp),再处理业务逻辑,你现在是先判断终止条件再进栈帧,会导致递归调用时$ra被覆盖 - 计算逻辑错误:C里是
余数 + 10*递归结果,你现在用multu $v0,$a0,这是把递归结果乘原数,完全不符合需求;而且addi $v0,$a1,10里$a1根本没赋值,属于无效操作 - 寄存器返回值错误:递归函数的返回值要存在
$v0里,你现在的计算没有正确把结果存到$v0
三、修正后的完整MIPS代码
我把代码修正好了,每一步都加了注释,你可以对照着看:
.data msg_input: .asciiz "Input => " msg_output: .asciiz "\nOutput => " .text .globl main main: # 先处理输入(这里用固定值99,和你的C代码一致,也可以改成读用户输入) li $a0, 99 # 把输入值99放到$a0,传给d2b函数 jal d2b # 调用递归函数d2b,返回值存在$v0 move $s0, $v0 # 保存返回的二进制数到$s0 # 打印输入值 li $v0, 4 la $a0, msg_input syscall li $v0, 1 li $a0, 99 syscall # 打印输出值 li $v0, 4 la $a0, msg_output syscall li $v0, 1 move $a0, $s0 syscall # 退出程序 li $v0, 10 syscall # 递归函数d2b:输入$a0是十进制数,返回值$v0是二进制数(以整数形式) d2b: # 终止条件:如果输入是0,返回0 li $t0, 0 beq $a0, $t0, return_zero # 建立栈帧:保存$ra、$fp、当前$a0的值(因为递归调用会覆盖$a0) subu $sp, $sp, 16 # 分配16字节栈空间 sw $ra, 12($sp) # 保存返回地址 sw $fp, 8($sp) # 保存帧指针 sw $a0, 4($sp) # 保存当前的十进制数d addiu $fp, $sp, 12 # 设置帧指针 # 计算d/2和d%2 li $t1, 2 div $a0, $t1 # $a0除以2,余数在hi,商在lo mfhi $t2 # $t2 = d%2(余数) mflo $a0 # $a0 = d/2(商),作为递归调用的参数 jal d2b # 递归调用d2b(d/2),返回值在$v0 # 计算:d%2 + 10*d2b(d/2) li $t3, 10 mult $v0, $t3 # 10*递归结果,结果在hi和lo mflo $v0 # 取低32位到$v0 add $v0, $v0, $t2 # 加上余数,得到当前层的返回值 # 恢复栈帧 lw $a0, 4($sp) # 恢复原来的d lw $fp, 8($sp) # 恢复帧指针 lw $ra, 12($sp) # 恢复返回地址 addiu $sp, $sp, 16 # 释放栈空间 jr $ra # 返回上层 # 终止条件的返回点:返回0 return_zero: li $v0, 0 jr $ra
额外说明
如果要改成用户输入十进制数,把main里的li $a0,99改成:
li $v0,5 syscall move $s1,$v0 # 保存输入值到$s1 move $a0,$s1
同时打印输入值的时候,把li $a0,99改成move $a0,$s1即可。
内容的提问来源于stack exchange,提问作者Q.John
相关产品推荐
相关产品推荐

