MIPS汇编实现快速排序时栈内存被partition覆盖致数组异常求助
MIPS快速排序递归问题:数组被覆盖的解决思路
看起来你在实现MIPS版快速排序的qsort递归函数时,踩了栈帧管理的典型大坑——这种问题在手写汇编递归时太容易犯了!你的swap已经验证正常,partition是给定的可靠代码,问题完全出在qsort的递归栈管理和参数处理上,下面给你详细分析和修正方案:
核心问题定位
你的自定义RetAddrMng栈管理逻辑完全不符合MIPS的调用规范,这是导致数组被覆盖的根本原因:
- 栈生长方向搞反了:MIPS的栈是向下生长的(栈指针
$sp必须递减来分配新空间),但你用add $sp, $sp, $t6让栈指针向上移动,会直接覆盖栈上方的内存区域(包括递归时需要保存的参数、返回地址,甚至可能干扰到数据段的数组) - 临时寄存器滥用:你用
$t6作为递归计数,但$t0-$t9是MIPS的临时寄存器,调用其他函数(比如partition)时会被随意覆盖,导致栈偏移计算彻底混乱,返回地址保存的位置完全错误
正确的递归栈帧实现方案
MIPS递归函数的标准写法必须遵循调用约定,下面是qsort的正确实现框架,完全适配你的现有代码:
qsort: # 1. 分配栈空间,保存需要保留的寄存器 # 我们需要保存$ra(返回地址)、$s0(数组指针v)、$s1(n)、$s2(partition返回的p) addiu $sp, $sp, -16 # 4个寄存器×4字节=16字节栈空间 sw $ra, 12($sp) # 保存返回地址到栈 sw $s0, 8($sp) # 保存原数组指针$a0到$s0(调用者保存寄存器,必须保留) sw $s1, 4($sp) # 保存原n值$a1到$s1 sw $s2, 0($sp) # 预留位置存partition返回的p # 2. 递归终止条件:n <=1 直接返回 ble $a1, 1, qsort_exit # 3. 调用partition函数,获取p值 move $a0, $s0 # 传入数组指针v move $a1, $s1 # 传入n jal partition move $s2, $v0 # 把partition返回的p保存到$s2(避免被后续调用覆盖) # 4. 第一个递归调用:qsort(v, p) move $a0, $s0 # 参数1:原数组v move $a1, $s2 # 参数2:p jal qsort # 5. 第二个递归调用:qsort(&v[p+1], n-p-1) sll $t0, $s2, 2 # 计算p×4(字节偏移量) add $a0, $s0, $t0 # 得到v[p]的地址 addiu $a0, $a0, 4 # 偏移4字节,得到&v[p+1] sub $a1, $s1, $s2 # 计算n-p addiu $a1, $a1, -1 # 计算n-p-1 jal qsort qsort_exit: # 6. 恢复寄存器,释放栈空间 lw $s2, 0($sp) lw $s1, 4($sp) lw $s0, 8($sp) lw $ra, 12($sp) addiu $sp, $sp, 16 # 栈指针恢复到进入函数前的位置 jr $ra # 返回调用者
关键规则说明(必须牢记)
- 寄存器分类:
$s0-$s7是调用者保存寄存器,如果你的函数要使用这些寄存器,必须先保存到栈里,退出时恢复;$t0-$t9是临时寄存器,调用其他函数后值会被清空,不能用来保存需要跨函数调用的数据 - 栈操作规范:分配栈空间必须用
addiu $sp, $sp, -size(向下生长),释放时用addiu $sp, $sp, size,绝对不能反向操作 - 递归参数传递:每次递归调用前,必须重新设置
$a0和$a1为当前递归的参数,不能依赖之前的寄存器值——因为之前的函数调用会修改这些参数寄存器
测试建议
- 先注释掉两个递归调用,只测试
partition的调用逻辑,确认partition返回的p正确且数组没有被破坏 - 逐步添加第一个递归调用,验证数组前半部分排序正常
- 最后添加第二个递归调用,验证完整排序功能
内容的提问来源于stack exchange,提问作者lightspot21
相关产品推荐
相关产品推荐

