MIPS汇编字符串反转中的加载与存储字节问题排查
尝试用MIPS汇编实现字符串反转功能,但作为汇编新手不会调试。当前代码无法反转测试字符串,还触发运行错误:
Exception occurred at PC=0x00000004
Bad address in text read: 0x00000005
控制台提示存在错误的指令地址,无法定位问题,猜测可能出在lb和sb指令的使用上。
相关代码
reverse_string子程序
## $a0 --> address of string ## $v0 --> string length reverse_string: j string_length # this subroutine works la $t3, STR_str # contains the string div $t9, $v0, 2 # divide string length by 2 add $t8, $zero, $zero # this is the counter sub $v0, $v0, 1 # string length - 1 add $t7, $t3, $v0 # offset by string length - 1 to find last character j reverse_string_loop reverse_string_loop: beq $t8, $t9, end_for_all # check if it has reached the halfway point lb $t1, 0($t7) # load last character into $t1 lb $t2, 0($t3) # load current character into $t2 sb $t1, 0($t3) # store last character at current location sb $t2, 0($t7) # store current character at last location add $t3, $t3, 1 sub $t7, $t7, 1 add $t8, $t8, 1
主程序
### reverse_string ## li $v0, 4 la $a0, STR_reverse_string syscall la $a0, STR_str jal reverse_string la $a0, STR_str jal print_test_string lw $ra, 0($sp) # POP return address addi $sp, $sp, 4 jr $ra
问题分析与修复
1. 无条件跳转导致后续代码被跳过
reverse_string开头的j string_length是致命错误:j指令会直接跳转到string_length,但不会保存返回地址,执行完string_length后不会回到reverse_string的后续代码(la $t3...这些行永远不会执行)。
修复:用jal string_length替代j string_length,jal会自动把返回地址存入$ra,执行完子程后可以继续执行后续代码。
2. 硬编码字符串地址,忽略传入参数
子程序定义里说明$a0是字符串地址,但代码里直接用la $t3, STR_str硬编码,没有使用传入的$a0,导致子程序无法处理主程序传入的字符串。
修复:把la $t3, STR_str改成move $t3, $a0,使用主程序传入的字符串地址。
3. 循环缺少跳转,执行一次后跑飞
reverse_string_loop执行完一次循环体后,没有跳转回循环开头的指令,会继续往下执行内存中后续的非法指令,触发地址错误。
修复:在循环体最后添加j reverse_string_loop,保证循环能重复执行。
4. 未定义end_for_all标签
代码里用beq $t8, $t9, end_for_all跳转,但没有定义该标签,会导致跳转到非法地址。
修复:添加end_for_all标签,并在标签处添加返回主程序的指令:
end_for_all: jr $ra
5. 寄存器使用注意事项
确保string_length子程序正确将字符串长度返回至$v0,且没有破坏$t3等需要使用的寄存器(如果string_length使用了临时寄存器,需要提前保存)。
修复后的reverse_string子程序示例
reverse_string: jal string_length # 调用子程并保存返回地址 move $t3, $a0 # 使用传入的字符串地址,而非硬编码 div $t9, $v0, 2 # 计算循环次数(长度/2) add $t8, $zero, $zero # 初始化计数器 sub $v0, $v0, 1 # 最后一个字符的索引(长度-1) add $t7, $t3, $v0 # 最后一个字符的地址 j reverse_string_loop reverse_string_loop: beq $t8, $t9, end_for_all # 到达中点则退出循环 lb $t1, 0($t7) # 加载末尾字符 lb $t2, 0($t3) # 加载当前字符 sb $t1, 0($t3) # 交换存储 sb $t2, 0($t7) add $t3, $t3, 1 # 指针右移 sub $t7, $t7, 1 # 指针左移 add $t8, $t8, 1 # 计数器+1 j reverse_string_loop # 回到循环开头 end_for_all: jr $ra # 返回主程序
内容的提问来源于stack exchange,提问作者user23033721

