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
beqto check if our inputnis 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 ton-1and usejal(jump and link) to call the factorial function.jalautomatically 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, usesw(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:
- 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.
- Pointer Functions: Use
lwto dereference pointers (read values) andswto assign through pointers (write values). Remember that pointer arguments are addresses, not values.
内容的提问来源于stack exchange,提问作者Vinson Thomas
相关产品推荐
相关产品推荐

