MARS汇编递归求LCS函数遇内存越界错误,求解决方案
MARS汇编LCS程序内存越界问题修复
问题描述
汇编操作成功完成,但运行时触发内存越界错误:
C:\Users\HP Laptop\Downloads\Assembly Assignment 2,\assign2_lcs.s第17行:运行时异常,地址0x00400010处访问地址0x00000000越界
错误原因分析
- 错误的指针加载逻辑:
find_lcs函数开头的lw $a0, 0($a0)和lw $a1, 0($a1)完全错误。从main传入的$a0/$a1已经是字符串的直接内存地址,而非指针的指针,用lw去读取该地址的内容(实际是字符串的第一个字符),会把字符值当作内存地址访问,直接导致非法内存访问。 - 违反MIPS调用约定:函数中使用了
$s0/$s1寄存器,但未在函数开头保存、结尾恢复,会破坏调用者的寄存器状态。 - 硬编码内存地址:直接使用
0x10010000这类固定地址,容易和MARS的内存布局冲突,也不符合规范。
修正后的完整代码
.data prompt1: .asciiz "Enter the first string (max 1024 characters): " prompt2: .asciiz "Enter the second string (max 1024 characters): " result: .asciiz "The length of LCS is: " # 定义字符串缓冲区,避免硬编码地址 str1_buffer: .space 1024 str2_buffer: .space 1024 .text .globl find_lcs find_lcs: # Prologue:保存需要用到的寄存器,包括$s0/$s1 addi $sp, $sp, -20 sw $ra, 0($sp) sw $a0, 4($sp) sw $a1, 8($sp) sw $s0, 12($sp) sw $s1, 16($sp) # 移除错误的lw指令,$a0/$a1已经是字符串的直接地址 # 防御性检查:判断传入地址是否为空 beqz $a0, end_lcs beqz $a1, end_lcs # Base case: 任一字符串为空则返回0 lb $t0, 0($a0) beq $t0, $zero, end_lcs lb $t0, 0($a1) beq $t0, $zero, end_lcs # 比较两个字符串的首字符 lb $t0, 0($a0) lb $t1, 0($a1) beq $t0, $t1, equal_chars # 字符不相等时,分两种情况递归计算 # 情况1:去掉s1的首字符 addi $a0, $a0, 1 jal find_lcs move $s0, $v0 # 情况2:去掉s2的首字符 lw $a0, 4($sp) # 恢复s1的原始地址 addi $a1, $a1, 1 jal find_lcs move $s1, $v0 # 取两种情况的最大值 bge $s0, $s1, case1_result move $v0, $s1 j end_lcs case1_result: move $v0, $s0 j end_lcs equal_chars: # 字符相等时,递归计算剩余子串的LCS并+1 addi $a0, $a0, 1 addi $a1, $a1, 1 jal find_lcs addi $v0, $v0, 1 j end_lcs end_lcs: # Epilogue:恢复保存的寄存器 lw $ra, 0($sp) lw $a0, 4($sp) lw $a1, 8($sp) lw $s0, 12($sp) lw $s1, 16($sp) addi $sp, $sp, 20 jr $ra # 测试主函数 main: # 提示输入第一个字符串 li $v0, 4 la $a0, prompt1 syscall # 读取第一个字符串 li $v0, 8 la $a0, str1_buffer li $a1, 1024 syscall # 去除字符串末尾的换行符(避免影响LCS计算) move $t0, $a0 remove_newline1: lb $t1, 0($t0) bne $t1, 10, remove_newline1_cont sb $zero, 0($t0) remove_newline1_cont: addi $t0, $t0, 1 bnez $t1, remove_newline1 # 提示输入第二个字符串 li $v0, 4 la $a0, prompt2 syscall # 读取第二个字符串 li $v0, 8 la $a0, str2_buffer li $a1, 1024 syscall # 去除字符串末尾的换行符 move $t0, $a0 remove_newline2: lb $t1, 0($t0) bne $t1, 10, remove_newline2_cont sb $zero, 0($t0) remove_newline2_cont: addi $t0, $t0, 1 bnez $t1, remove_newline2 # 调用find_lcs计算LCS长度 la $a0, str1_buffer la $a1, str2_buffer jal find_lcs # 输出结果提示 li $v0, 4 la $a0, result syscall # 输出LCS长度 move $a0, $v0 li $v0, 1 syscall # 程序退出 li $v0, 10 syscall
关键修改点
- 移除错误的指针加载:删掉
lw $a0, 0($a0)和lw $a1, 0($a1),直接使用传入的字符串地址。 - 遵守调用约定:在函数栈帧中保存
$s0和$s1,避免破坏调用者寄存器。 - 替换硬编码地址:在
.data段定义str1_buffer和str2_buffer,用符号地址替代固定内存值。 - 添加换行符处理:读取字符串后去除末尾的换行符,避免换行符被当作有效字符参与LCS计算。
内容的提问来源于stack exchange,提问作者Sulais
相关产品推荐
相关产品推荐

