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

MIPS初学者求助:将递归、带指针的C函数转为QtSPIM可用MIPS代码并附详解

Hey there! As someone who’s stumbled through MIPS assembly with QtSPIM early on, I know how critical clear, well-commented examples are when you’re learning to translate C to MIPS. Let’s break down two common functions—a recursive factorial and a pointer-based swap—into QtSPIM-compatible assembly, with line-by-line explanations so you can apply this logic to other functions later.


Recursive Factorial Function (C → MIPS)

First, here’s the simple recursive factorial function we’ll convert:

int factorial(int n) {
    if (n == 0) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

MIPS Assembly with Detailed Comments

# Recursive factorial for QtSPIM
# Input: $a0 = integer n
# Output: $v0 = factorial(n)

factorial:
    # Prologue: Set up stack frame to preserve registers
    addi    $sp, $sp, -8       # Allocate 8 bytes on stack (2 words: $ra, $s0)
    sw      $ra, 4($sp)        # Save return address (we need it for recursion)
    sw      $s0, 0($sp)        # Save $s0 (we'll store the original n here)

    # Check base case: if n == 0, return 1
    beq     $a0, $zero, base_case  # Branch to base case if input is 0
    j       recursive_case          # Otherwise jump to recursive logic

base_case:
    li      $v0, 1             # Load return value 1 into $v0 (MIPS uses $v0 for returns)
    j       epilogue           # Jump to clean up stack before returning

recursive_case:
    move    $s0, $a0           # Store original n in $s0 (we'll modify $a0 next)
    addi    $a0, $a0, -1       # Decrement n by 1 (prepare argument for factorial(n-1))
    jal     factorial          # Call factorial(n-1); $ra gets updated to return here

    # Multiply original n by recursive result
    mul     $v0, $s0, $v0      # $v0 = original n * factorial(n-1)

epilogue:
    # Restore saved registers and clean up stack
    lw      $s0, 0($sp)        # Get back our original n value
    lw      $ra, 4($sp)        # Restore the return address
    addi    $sp, $sp, 8        # Deallocate the 8 bytes we reserved
    jr      $ra                # Jump back to the caller

Key Explanations:

  • Prologue: MIPS calling conventions require us to save saved registers (like $s0) and the return address ($ra) on the stack if we modify them—this ensures they retain their values when we call other functions (including the recursive call).
  • Base Case Handling: We use beq to check if our input n is 0. If yes, we immediately set the return value to 1 and jump to clean up the stack.
  • Recursive Call: After saving the original n, we adjust the input to n-1 and use jal (jump and link) to call the factorial function. jal automatically saves the current return address to $ra, which we’ll need to get back to this point after the recursion finishes.
  • Epilogue: Always restore the registers you saved and deallocate the stack space before returning—this prevents stack leaks and ensures the caller’s environment remains intact.

Pointer-Based Swap Function (C → MIPS)

Next, let’s convert a classic swap function that uses pointers:

void swap(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

MIPS Assembly with Detailed Comments

# Pointer-based swap for QtSPIM
# Input: $a0 = pointer to integer x, $a1 = pointer to integer y

swap:
    # Prologue: Allocate stack space for temp variable
    addi    $sp, $sp, -4       # Reserve 4 bytes (1 word) for our temp variable
    sw      $t0, 0($sp)        # Save $t0 (temporary register) as a best practice

    # Step 1: temp = *x
    lw      $t0, 0($a0)        # Load the value at address $a0 (x's value) into $t0
    sw      $t0, 0($sp)        # Store $t0 in stack (this is our temp)

    # Step 2: *x = *y
    lw      $t1, 0($a1)        # Load value at address $a1 (y's value) into $t1
    sw      $t1, 0($a0)        # Write $t1 into address $a0 (update x's value)

    # Step 3: *y = temp
    lw      $t0, 0($sp)        # Load temp from stack back into $t0
    sw      $t0, 0($a1)        # Write $t0 into address $a1 (update y's value)

    # Epilogue: Clean up and return
    lw      $t0, 0($sp)        # Restore $t0 (optional, but good practice)
    addi    $sp, $sp, 4        # Deallocate the 4 bytes we reserved
    jr      $ra                # Return to caller

Key Explanations:

  • Pointers in MIPS: The input registers $a0 and $a1 hold the memory addresses of x and y, not their actual values. To read from a pointer, use lw (load word) with the address register. To write to a pointer, use sw (store word) to put a value into that address.
  • Temp Variable: We use the stack to store our temp value since we need to hold onto x’s original value while we overwrite it with y’s value.
  • Temporary Registers: While $t0/$t1 are temporary registers (callers don’t expect them to retain values), saving $t0 is a good habit to avoid accidental overwrites if we added more logic later.

Quick Takeaways for Future Conversions:

  1. Recursive Functions: Always save $ra and any saved registers you use on the stack. Handle base cases first, and never skip cleaning up the stack before returning.
  2. Pointer Functions: Use lw to dereference pointers (read values) and sw to assign through pointers (write values). Remember that pointer arguments are addresses, not values.

内容的提问来源于stack exchange,提问作者Vinson Thomas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:15:26