MIPS汇编程序运行报错Runtime exception地址越界,请求排查
MIPS汇编程序地址越界错误排查与修复
问题描述
编写了一段实现读取用户字符串并支持修改至退出功能的MIPS汇编程序,运行时触发错误:
Error code: Runtime exception at 0x00400010: address out of range 0x00000000
错误发生在swap_characters函数loop段的lb $t0, 0($s0)指令处,程序无法正常运行。
原代码
.data prompt1: .asciiz "This program reads in a String from the user and then allows the user to make changes to it until they want to stop.\n" prompt2: .asciiz "Please enter your string now (maximum of 40 characters):\n" prompt3: .asciiz "Your current string is:\n" prompt4: .asciiz "Do you want to make any changes to the string? (Y/N): " prompt5: .asciiz "Enter the character in the string you would like replaced: " prompt6: .asciiz "Enter what you would like to change the character to: " prompt7: .asciiz "Your final string is:\n" .text .globl main swap_characters: addi $sp, $sp, -8 sw $ra, 4($sp) sw $s0, 0($sp) move $s0, $a0 loop: lb $t0, 0($s0) beqz $t0, done beq $t0, $a1, replace addiu $s0, $s0, 1 j loop replace: sb $a2, 0($s0) addiu $s0, $s0, 1 j loop done: lw $ra, 4($sp) lw $s0, 0($sp) addi $sp, $sp, 8 jr $ra main: li $v0, 4 la $a0, prompt1 syscall li $v0, 4 la $a0, prompt2 syscall li $v0, 8 la $a0, 0($v1) li $a1, 40 syscall li $v0, 4 la $a0, prompt3 syscall li $v0, 4 la $a0, 0($v1) syscall change_loop: # Prompt for making changes li $v0, 4 la $a0, prompt4 syscall li $v0, 12 syscall beqz $v0, done1 li $v0, 4 la $a0, prompt5 syscall li $v0, 12 syscall move $a1, $v0 li $v0, 4 la $a0, prompt6 syscall li $v0, 12 syscall move $a2, $v0 move $a0, $v1 jal swap_characters li $v0, 4 la $a0, prompt3 syscall # Print the updated string li $v0, 4 la $a0, 0($v1) syscall j change_loop done1: li $v0, 4 la $a0, prompt7 syscall li $v0, 4 la $a0, 0($v1) li $v0, 10 syscall
问题根源
- 未分配字符串缓冲区:程序没有在
.data段为用户输入的字符串预留内存空间,导致读取字符串时没有合法的存储地址。 - 错误使用
$v1寄存器:系统调用li $v0,8(读取字符串)要求$a0指向预分配的内存,但代码中la $a0, 0($v1)将$a0设为0($v1初始值为0),非法写入地址0的内存区域。后续所有通过0($v1)访问字符串的操作,本质都是访问地址0,最终在swap_characters中执行lb指令时触发地址越界。 - 退出逻辑错误:
beqz $v0, done1判断无效,因为li $v0,12读取的是字符ASCII值,输入'N'的ASCII为78,不会是0。
修复方案
1. 添加字符串缓冲区
在.data段末尾添加:
input_str: .space 41 # 分配41字节,容纳40字符+字符串结束符'\0'
2. 修正字符串读取逻辑
将main中读取字符串的代码:
li $v0, 8 la $a0, 0($v1) li $a1, 40 syscall
替换为:
li $v0, 8 la $a0, input_str # 指向预分配的缓冲区 li $a1, 41 # 包含结束符,所以长度设为41 syscall
3. 替换所有非法的字符串引用
将所有la $a0, 0($v1)和move $a0, $v1替换为指向input_str的引用:
- 第一次打印输入字符串:
li $v0,4; la $a0, input_str; syscall - 调用
swap_characters前:la $a0, input_str - 打印更新后字符串:
li $v0,4; la $a0, input_str; syscall - 打印最终字符串:
li $v0,4; la $a0, input_str; syscall
4. 处理字符输入的换行残留
使用li $v0,12读取字符后,输入缓冲区会残留换行符,可能导致后续输入异常。在每次读取字符后添加清除逻辑:
li $v0, 12 syscall # 清除换行符 li $v0, 12 syscall
5. 修正退出逻辑
将beqz $v0, done1替换为:
beq $v0, 'N', done1 # 判断输入是否为'N',触发退出
修复后的完整代码
.data prompt1: .asciiz "This program reads in a String from the user and then allows the user to make changes to it until they want to stop.\n" prompt2: .asciiz "Please enter your string now (maximum of 40 characters):\n" prompt3: .asciiz "Your current string is:\n" prompt4: .asciiz "Do you want to make any changes to the string? (Y/N): " prompt5: .asciiz "Enter the character in the string you would like replaced: " prompt6: .asciiz "Enter what you would like to change the character to: " prompt7: .asciiz "Your final string is:\n" input_str: .space 41 # 预分配字符串缓冲区 .text .globl main swap_characters: addi $sp, $sp, -8 sw $ra, 4($sp) sw $s0, 0($sp) move $s0, $a0 loop: lb $t0, 0($s0) beqz $t0, done beq $t0, $a1, replace addiu $s0, $s0, 1 j loop replace: sb $a2, 0($s0) addiu $s0, $s0, 1 j loop done: lw $ra, 4($sp) lw $s0, 0($sp) addi $sp, $sp, 8 jr $ra main: li $v0, 4 la $a0, prompt1 syscall li $v0, 4 la $a0, prompt2 syscall li $v0, 8 la $a0, input_str li $a1, 41 syscall li $v0, 4 la $a0, prompt3 syscall li $v0, 4 la $a0, input_str syscall change_loop: # Prompt for making changes li $v0, 4 la $a0, prompt4 syscall li $v0, 12 syscall # 清除换行符 li $v0, 12 syscall beq $v0, 'N', done1 li $v0, 4 la $a0, prompt5 syscall li $v0, 12 syscall # 清除换行符 li $v0, 12 syscall move $a1, $v0 li $v0, 4 la $a0, prompt6 syscall li $v0, 12 syscall # 清除换行符 li $v0, 12 syscall move $a2, $v0 la $a0, input_str jal swap_characters li $v0, 4 la $a0, prompt3 syscall # Print the updated string li $v0, 4 la $a0, input_str syscall j change_loop done1: li $v0, 4 la $a0, prompt7 syscall li $v0, 4 la $a0, input_str li $v0, 10 syscall
内容的提问来源于stack exchange,提问作者Kraken
相关产品推荐
相关产品推荐

