You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于递归的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,而且逻辑完全颠倒:

  1. 终止条件搞反:C代码里d==0时返回0,但你的MIPS代码中,当$a0==0时跳去skip执行递归,反而直接jr $ra返回0,逻辑完全错误
  2. 栈帧操作时机错误:应该先建立栈帧(分配栈空间、保存$ra和$fp),再处理业务逻辑,你现在是先判断终止条件再进栈帧,会导致递归调用时$ra被覆盖
  3. 计算逻辑错误:C里是余数 + 10*递归结果,你现在用multu $v0,$a0,这是把递归结果乘原数,完全不符合需求;而且addi $v0,$a1,10里$a1根本没赋值,属于无效操作
  4. 寄存器返回值错误:递归函数的返回值要存在$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:03:19