String 如何通过替换地址中的内存而不是在MIPS程序集中创建新内存来正确反转字符串

String 如何通过替换地址中的内存而不是在MIPS程序集中创建新内存来正确反转字符串,string,assembly,replace,mips,reverse,String,Assembly,Replace,Mips,Reverse,我在计算机体系结构课上有一个作业,我们要完成一系列的任务。最后一项任务是创建一个子例程,该子例程接受字符串并将其反转。我们不允许使用原始字符串来创建“新内存字符串”。相反,我们应该逐渐替换原始字符串的内容 我的第一个想法是在临时寄存器中加载最左边和最右边的字符,然后使用它们“交换”它们的位置。完成后,地址应该递增和递减(有两个地址,一个指向字符串的开头,另一个指向字符串的结尾)。然后循环,直到两个地址都指向同一个点,然后循环结束 这是我的反向字符串子程序的代码$a0是以NULL结尾的字符串的地址

我在计算机体系结构课上有一个作业,我们要完成一系列的任务。最后一项任务是创建一个子例程,该子例程接受字符串并将其反转。我们不允许使用原始字符串来创建“新内存字符串”。相反,我们应该逐渐替换原始字符串的内容

我的第一个想法是在临时寄存器中加载最左边和最右边的字符,然后使用它们“交换”它们的位置。完成后,地址应该递增和递减(有两个地址,一个指向字符串的开头,另一个指向字符串的结尾)。然后循环,直到两个地址都指向同一个点,然后循环结束

这是我的反向字符串子程序的代码$a0是以NULL结尾的字符串的地址。我不确定我脑子里想的算法是否正确地转化为MIPS,我对这种语言非常陌生

reverse_string:

    #### Write your solution here ####
    beqz    $a0, rs_exit        # Check if string is NULL, exit if it is
    li  $t0, 0
    li  $t1, 0
    add $t0, $t0, $a0       # Save leftmost adress of string
    add $t1, $a0, $v0       # Save rightmost adress of string

    rs_loop:
    beq $t0, $t1, rs_exit
    lbu $t2, 0($t0)     # Save leftmost character in temporary register
    lbu $t3, 0($t1)     # Save rightmost character in temporary register
    sb  $a0, 0($t2)     # Replace rightmost character with leftmost character
    sb  $a0, 0($t3)     # Replace leftmost character with rightmost character
    addi    $t0, $t0, 1     # Increment leftmost adress by 1
    subi    $t1, $t1, 1     # Decrement rightmost by 1
    j   rs_loop

    rs_exit:
    jr  $ra
main中用于执行反向_字符串的代码如下:

    ##
    ### reverse_string
    ##

    li  $v0, 4
    la  $a0, STR_reverse_string
    syscall

    la  $a0, STR_str
    la  $a1, reverse_string
    jal reverse_string

    la  $a0, STR_str
    jal print_test_string
所以,正如前面提到的。预期结果是程序应该打印出反向字符串。目前,我在以下行出现错误:

sb  $a0, 0($t2)     # Replace rightmost character with leftmost character
错误:

Runtime exception at 0x004000c4: address out of range 0x0000004a
我已经试了好几个小时了。有几个人成功地获得了类似问题的帮助,但是他们有点不同(来自用户的输入,并且他们创建了新字符串,而不是替换原始字符串的内容)

谢谢你的帮助!多谢各位

我的第一个想法是。。。这将循环,直到两个地址都指向同一个点

直到“结束”指针等于或小于“开始”指针。对于“abcd”等偶数长度,指向“b”和“c”的指针是有效的,但在交换和递增+递减之后,它们仍然不相等,但应该结束循环。不管怎样,除了这个细节,你的想法是好的

0x004000c4处的运行时异常:地址超出范围0x0000004a

这意味着
sb
(存储字节)指令尝试在内存地址
0x0000004a
处写入,该地址不可访问(没有内存,或者没有足够的权限让您的进程在那里写入)。这意味着
0($t2)
没有计算到该地址,这意味着
t2
中的值等于0x4a。当单步执行指令时,您应该能够在调试器中看到这一点

从那时起,您必须追溯整个操作,它是如何变成这个值的,以及为什么


为什么设置了
a1
?任务中的契约表示字符串地址是在
a0
中传递的,没有其他内容。但无论如何,这不是你的密码,只是好奇。。。那么让我们开始你的代码

空测试正常,您也可以使用
addi
$zero=$0
作为零值,即:

    beqz    $a0, rs_exit  # Check if string is NULL, exit if it is
    add $t0, $a0, $zero   # t0 = left pointer (start of string)
    add $t1, $a0, $v0     # t1 = start of string plus unknown value in v0
正如您在第二条评论中看到的,您在开始时就有一个bug

这确实意味着您根本没有调试代码,或者输入非常有限

如果例程的唯一输入是字符串的地址,则必须逐个字符地查找存储终止零的位置,并使用该位置来计算“end”指针。 (例如,您可以将
a0
复制到
a1
,从
a1
加载字节,检查零,递增
a1
,然后再次提取,……直到找到终止零为止……第一个零前面的地址(-1)是您的“结束”指针)

让我们假设你有正确的指针。。。另一部分:

前两个是正确的(使用正确的指针)。但另外两个完全错了。“sb”存储字节有参数“value,memory address”,所以您试图将字符串地址的底部字节存储到由字符表示的内存地址
0x4a
采用ASCII编码字符
'J'
,正如错误消息所指出的,该字符不是有效的内存地址

你可能认为自己很幸运,因为当在程序集编程时,有时类似的错误实际上发生在登记错误的值中,这是可访问的,并且一些内存被覆盖了,而这些内存本来应该没有,但是没有崩溃或任何问题的迹象。然后过了很长时间,代码的另一部分可能会到达该内存,期望其他东西存储在那里,这将产生一些bug。这些“内存覆盖”错误极难破译和修复,任何一个老派汇编/C/C++程序员都可以告诉你

因此,您的代码非常脆弱,请尝试实现您描述的想法

试着用调试器运行它(如果你使用的是SPIM/MARS模拟器,它们都有内置的调试器,不是最先进的调试器,但可以用于这些微小的教程任务),试着完全理解正在发生的事情,以及为什么这些指令不代表你的想法,以及它们在实际中的实际操作

您必须学习这项技能,如果您想在汇编中编写代码,汇编不允许出现错误或一些模糊的解释,或者通过“尝试”一些东西、随机更改源代码来获得工作代码。始终要弄清楚代码的实际用途,以及它与您想要的有多大的不同。然后修理它

一般来说,显示“没有调试”的汇编问题会很快被否决,因为调试是一个耗时的过程,而仅仅将其外包给其他人是不礼貌的。。。但你们有一个额外的点,我清楚地说明了你们的想法,并提供了几乎完整的可复制的例子(您忘了显示字符串定义……在汇编中,您定义数据的方式往往比代码更重要,因此您可能也有某种缺陷,比如不向字符串添加零终止符等)

也不要试图猜测
beqz    $a0, rs_exit        # Check if string is NULL, exit if it is
li  $t0, 0
li  $t1, 0
add $t0, $t0, $a0       # Save leftmost adress of string
add $t1, $a0, $v0       # Save rightmost adress of string
    beqz    $a0, rs_exit  # Check if string is NULL, exit if it is
    add $t0, $a0, $zero   # t0 = left pointer (start of string)
    add $t1, $a0, $v0     # t1 = start of string plus unknown value in v0
lbu $t2, 0($t0)     # Save leftmost character in temporary register
lbu $t3, 0($t1)     # Save rightmost character in temporary register
sb  $a0, 0($t2)     # Replace rightmost character with leftmost character
sb  $a0, 0($t3)     # Replace leftmost character with rightmost character