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

MIPS汇编出现Runtime exception:地址未按字对齐问题如何解决?

MIPS汇编“fetch address not aligned on word boundary”错误修复

MIPS架构要求所有字操作(lw/sw指令)必须对齐到4字节边界,也就是内存地址的最后两位必须是00(二进制),对应十六进制末位为0、4、8、C。当代码尝试访问非对齐地址时,就会抛出这个运行时异常。

代码中的核心问题

  1. SwapTwoInts参数传递错误
    SwapTwoInts函数要求$a0和$a1传入的是整数的内存地址,但原代码中却把数组元素的值加载到$a0/$a1中:
lw $a0, 0($t3)          # 错误:加载的是intArr[i]的值,而非地址
lw $a1, 0($t5)       # 错误:加载的是intArr[j]的值,而非地址
jal SwapTwoInts

当这些值是奇数时,作为地址访问就会触发非对齐错误。

  1. ValidateInt参数传递错误
    ValidateInt函数要求$a0传入要验证整数的内存地址,但原代码未正确初始化$a0就调用函数,导致lw $t0, 0($a0)访问非法地址。

  2. ShowIntArray参数传递错误
    ShowIntArray需要三个参数:数组地址($a0)、数组大小($a1)、标签地址($a2),但原调用只传入了大小,导致lw $t1, 0($a0)访问错误地址。

  3. GetOneIntByVal错误信息打印逻辑错误
    原代码中先设置打印字符串的syscall($v0=4),但立刻改成打印字符($v0=11),导致错误信息无法输出。

  4. 缺失数组输入逻辑
    原代码没有向intArr中输入整数的步骤,直接交换空数组,属于逻辑漏洞。


修复后的完整代码

.data
.align 2                # 按字对齐后续数据
intArr:             .space 28            # 7个整数的数组(7*4字节)
vtdPrompt:          .asciiz "vals to do? "
entIntPrompt:       .asciiz "enter an int: "
adjMsg:             .asciiz " is bad, make it "
origLab:            .asciiz "original:\n"
backLab:            .asciiz "backward:\n"
dmPrompt:           .asciiz "do more? "
invalid_input_msg:  .asciiz "Invalid input. Please enter a valid integer.\n"
tempVal:            .word 0              # 新增:存储valsToDo的临时变量

.text
.globl main

main:
li $v0, 4            # 打印字符串syscall
la $a0, vtdPrompt    # 加载提示地址
syscall

li $v0, 4            # 打印original标签
la $a0, origLab
syscall

main_loop:
jal GetOneIntByVal   # 获取valsToDo
sw $v0, tempVal      # 将值存入tempVal,用于ValidateInt的地址参数
move $t0, $v0        # 保存valsToDo到$t0

# 修复ValidateInt参数传递
la $a0, tempVal      # 传入tempVal的地址(要验证的整数地址)
li $a1, 1            # 最小值
li $a2, 7            # 最大值
la $a3, adjMsg       # 提示信息地址
jal ValidateInt      # 验证valsToDo
lw $t0, tempVal      # 取回验证后的值

beq $t0, $zero, exit_program   # 输入0则退出

# 新增:向intArr输入整数
li $t1, 0            # 初始化i=0
input_loop:
beq $t1, $t0, end_input

li $v0, 4
la $a0, entIntPrompt
syscall

jal GetOneIntByVal   # 获取输入的整数
sll $t4, $t1, 2      # 计算偏移量
la $t3, intArr       # 加载数组基地址
add $t3, $t3, $t4    # 计算元素地址
sw $v0, 0($t3)       # 存入数组

addi $t1, $t1, 1
j input_loop
end_input:

# 打印原数组
la $a0, intArr
move $a1, $t0
la $a2, origLab
jal ShowIntArray

# 初始化交换循环变量
move $t1, $zero      # i=0
move $t2, $t0        # j=valsToDo

swap_loop:
beq $t1, $t2, show_backwards   # i >= j时退出循环

sll $t4, $t1, 2         # 计算intArr[i]的偏移量
la $t3, intArr          # 加载数组基地址
add $t3, $t3, $t4       # 计算intArr[i]的地址
move $a0, $t3           # 修复:传入intArr[i]的地址给SwapTwoInts

addi $t4, $t2, -1    # j-1
sll $t5, $t4, 2      # 计算intArr[j-1]的偏移量
la $t5_addr, intArr   # 加载数组基地址
add $t5_addr, $t5_addr, $t5  # 计算intArr[j-1]的地址
move $a1, $t5_addr       # 修复:传入intArr[j-1]的地址给SwapTwoInts

jal SwapTwoInts      # 交换两个元素

addi $t1, $t1, 1     # i++
addi $t2, $t2, -1    # j--
j swap_loop

show_backwards:
# 修复ShowIntArray参数传递
la $a0, intArr       # 数组地址
move $a1, $t0        # 数组大小
la $a2, backLab      # 标签地址
jal ShowIntArray     # 打印反转后的数组

li $v0, 4            # 打印do more提示
la $a0, dmPrompt
syscall

li $v0, 12           # 读取字符
syscall
move $a0, $v0        # 直接保存读取的字符(syscall 12返回值在$v0)

li $t6, 'n'
li $t7, 'N'
beq $a0, $t6, exit_program
beq $a0, $t7, exit_program

j main_loop 

exit_program:
li $v0, 10           # 退出程序
syscall

# 获取整数函数
GetOneIntByVal:
li $v0, 5            # 读取整数syscall
syscall

bltz $v0, invalid_input  # 负数视为无效
j input_valid

input_valid:
jr $ra                  # 返回

invalid_input:
# 修复错误信息打印顺序
li $v0, 4
la $a0, invalid_input_msg
syscall                # 先打印错误信息

li $v0, 11
li $a0, '\n'
syscall                # 再打印换行

j GetOneIntByVal       # 重新输入

# 验证整数函数
ValidateInt:
lw $t0, 0($a0)       # 加载要验证的整数
blt $t0, $a1, less_than_min
bgt $t0, $a2, greater_than_max

jr $ra               # 范围内则返回

less_than_min:
j set_min

greater_than_max:
j set_max

set_min:
sw $a1, 0($a0)       # 设置为最小值
jr $ra

set_max:
sw $a2, 0($a0)       # 设置为最大值
jr $ra

# 打印整数数组函数
ShowIntArray:
li $v0, 4
move $a0, $a2        # 打印标签
syscall

li $t0, 0            # k=0
la $t3, intArr       # 保存数组基地址(避免$a0被修改)

show_loop:
beq $t0, $a1, end_show

sll $t4, $t0, 2      # 计算偏移量
add $t5, $t3, $t4    # 计算元素地址
lw $t1, 0($t5)       # 加载元素值

li $v0, 1
move $a0, $t1
syscall               # 打印整数

li $v0, 11
li $a0, ' '
syscall               # 打印空格

addi $t0, $t0, 1     # k++
j show_loop

end_show:
li $v0, 11
li $a0, '\n'
syscall               # 打印换行

jr $ra               # 返回

# 交换两个整数函数
SwapTwoInts:
lw $t0, 0($a0)       # 加载第一个整数的值
lw $t1, 0($a1)       # 加载第二个整数的值

sw $t1, 0($a0)       # 保存第二个值到第一个地址
sw $t0, 0($a1)       # 保存第一个值到第二个地址

jr $ra               # 返回

关键修复细节

  • 新增tempVal变量,为ValidateInt提供合法的内存地址参数
  • SwapTwoInts调用时传入数组元素的地址而非值,确保lw/sw操作的地址对齐
  • ShowIntArray调用时正确传入数组地址、大小、标签地址三个参数
  • 修复GetOneIntByVal中错误信息的打印顺序,确保提示正常输出
  • 补充向intArr输入整数的逻辑,避免操作空数组

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:32:02