MIPS汇编实现连续指定小写字母对转大写并计数的问题
MIPS双字符匹配转换的常见问题排查
核心疑问解答:是否需要使用半字加载?
完全不需要强制使用半字加载处理双字符匹配场景。字符串是逐字节存储的,使用两次单字节lb指令读取相邻两个字符的方式逻辑更简单,不会触发MIPS非对齐内存访问的异常,也不需要考虑大小端序对寄存器值的影响,对新手更友好。
现有代码无转换效果的核心原因
逐行拆解现存硬伤:
- 最致命的逻辑断流:在
lig标号块加载完第一个字符t0之后,直接执行j next跳转。这导致跳转语句后面所有的字符比较、转换、计数代码都是永远不会执行的死代码,程序每次循环只会移动字符串指针,完全不执行任何匹配逻辑,自然没有任何转换效果。 - 内存地址访问错误:
- 即便删掉无效的
j next,加载第二个配对字符时使用的lb $t1,($a0)读的是和第一个字符完全相同的位置,没有偏移到下一个相邻字节,永远读不到第二个待匹配字符。 - 存储转换结果时,
sb $t1,($a0)同样是把转大写后的第二个字符覆盖到第一个字符的地址,两次存储指向同一个内存位置,等于白做转换。
- 即便删掉无效的
- 常量比较不匹配:代码残留了之前练习的
'f'字符比较逻辑,完全没有替换成需求里的'x',就算执行到比较步骤也永远匹配不到目标字符。 - 指针跳转逻辑混乱:匹配成功后如果顺着流程走到
next块,会把指针重复累加,跳过的字符数和预期不一致,还会导致重复计数、漏处理字符的问题。
正确的实现逻辑
按照需求(匹配连续的xx、xi组合,匹配后把两个字符转大写,统计匹配次数,输出效果符合示例),整体流程为:
- 初始化计数器
$v0为0,字符串指针$a0指向字符串起始位置。 - 进入循环:
- 加载当前指针位置的字符,遇到字符串结束符
\0直接退出。 - 如果当前字符不是小写
'x',直接把指针后移1位,进入下一轮循环(只有x开头的组合才需要匹配)。 - 如果当前字符是
'x',加载下一个相邻字节的字符:- 如果下一个字符是字符串结束符,直接把指针后移1位,进入下一轮循环(凑不齐两个字符无法匹配)。
- 判断下一个字符是
'x'还是'i':- 不匹配的话,指针后移1位,进入下一轮循环。
- 匹配的话,把两个字符都减去32转成大写,存回原内存位置,计数器加1,直接把指针后移2位(跳过已经处理的两个字符,不做重叠匹配),进入下一轮循环。
- 加载当前指针位置的字符,遇到字符串结束符
可直接运行的参考代码
lig: lb $t0, 0($a0) # 加载当前位置的第一个字符 beq $t0, $zero, exit # 遇到字符串结束符,退出循环 # 只有当前字符是'x',才有可能匹配目标组合 bne $t0, 'x', next_pos # 加载下一个相邻字符做配对判断 lb $t1, 1($a0) beq $t1, $zero, next_pos # 后面没有字符了,无法配对 # 判断下一个字符是'x'还是'i' bne $t1, 'x', check_i # 匹配到'xx'组合 sub $t0, $t0, 32 sub $t1, $t1, 32 sb $t0, 0($a0) sb $t1, 1($a0) addi $v0, $v0, 1 # 计数器+1 addi $a0, $a0, 2 # 直接跳2位,跳过已处理的两个字符 j lig check_i: bne $t1, 'i', next_pos # 匹配到'xi'组合 sub $t0, $t0, 32 sub $t1, $t1, 32 sb $t0, 0($a0) sb $t1, 1($a0) addi $v0, $v0, 1 # 计数器+1 addi $a0, $a0, 2 # 直接跳2位,跳过已处理的两个字符 j lig next_pos: addi $a0, $a0, 1 # 不匹配的情况,指针后移1位 j lig exit: jr $ra
注:参考代码的处理效果完全匹配示例规则,例如输入
"abcdxx"会输出"abcdXX",计数为1。按照不重叠匹配规则,输入"xxxx xixi"会输出"XXXX XIXI",总计数为4;如果需要调整为连续同组合只匹配第一次的效果,修改对应位置的指针步长即可适配。
内容的提问来源于stack exchange,提问作者Student 1
相关产品推荐
相关产品推荐

