向后读取字符串并保存它(字符串逆转带有扭曲)



我是新来的,但我会尽力遵循准则。请注意,我只是从MIPS和MARS开始,因此,如果我在做一些愚蠢的事情,请让我知道如何修复它,以便我可以改进。

我目前正在为我的长距离汇编课程进行学校任务,在那里我必须扭转字符串。捕获是我不能创建一个新的字符串,而必须修改现有的字符串。

我想到为什么我可以在字符串中找到第一个 n/last-n字符,而我可以从字符串中的最后一个字符向后走到字符串的第一个字符。我设法向后阅读和打印字符串,但现在我卡住了,可以在正确的方向上使用轻推。

如果您运行此代码,则将打印字符串" dlrow olleh",并且该程序将退出,到目前为止一切都很好(我希望)。我的问题是:

如何以反向格式保存字符串到原始标签" str"?

我开始编写一些代码,但尚未完成,请参阅我的问题与之相关的部分"反向"标签。到目前为止,这是我的代码:请注意,我不能使用任何库,我必须手动这样做。

请参阅下面的编辑

.data
str: .asciiz "Hello World"
str_msg1: .asciiz "String: "
str_msg2: .asciiz "String lenght: "
str_msg3: .asciiz "String reversed: "
str_nl: .asciiz "n"
strLen: .space 8
.text
# Printing the original string
la $a0,str_msg1
li $v0,4
syscall
la $a0,str
li $v0,4
syscall
la $a0,str_nl
li $v0,4
syscall
#Get the lenght of the string
la $a0,str
getLen:
    lb $t0,0($a0)
    beqz $t0,printLen
    addi $t1,$t1,1
    addi $a0,$a0,1
    j getLen
#saves and prints the lenght of the string
printLen:
    sb $t1,strLen
    la $a0,str_msg2
    li $v0,4
    syscall
    lb $a0,strLen
    li $v0, 1
    syscall
    la $a0,str_nl
    li $v0,4
    syscall

这是有趣的部分:

reverse:
    addi $t0,$zero,0 #zeroing the t registers to clear them for the next part.
    addi $t1,$zero,0
    addi $t2,$zero,0
    la $a1,str             #contains the adress of the string
    lb $a2,strLen          #contains the lenght of the string 
    add $a1,$a1,$a2          #adds the lenght of the string to the adress meaning it now stores the last position of the string
    add $t0,$a2,$zero    #counter initiated to lengt of the string
    loop:
        subi $a1,$a1,1  #decrement the adress since we dont want the null terminator string
        beqz $t0,exit      #if the counter variable is zero we have gone over the entire range of the strings index,then exit
        subi $t0,$t0,1  #decrement the counter since if we are here then we have not reached the end yet
                          #<temporary print statement below is for debugging purposes>
        lb $a0,0($a1)   #loads the first byte from the adress stored at $a1 which will be the string in decending order
        li $v0,11         #print character syscall
        syscall
        j loop
# Exit the program
exit:
    li $v0,10
    syscall

感谢您抽出宝贵的时间阅读我的问题。欢呼!

编辑

所以我可能已经找到了一个答案,我认为我应该发布它,以便其他人可以阅读它,也许可以改进它。从技术上讲,我正在打击此任务的允许,但最终将原始字符串更改为相反的格式,所以我认为应该没关系。我通过创建一个缓冲区来做到这一点(strbuffer .Space 255)然后我以相反的方式复制了字符串,然后将其存储在原始字符串的地址上。它似乎有效,我很欣喜若狂!

我的更改代码看起来像这样:

reverse:
    addi $t0,$zero,0 #zeroing all the t registers to clear them for the next part.
    addi $t1,$zero,0
    addi $t2,$zero,0
    addi $t3,$zero,0
    addi $t4,$zero,0
    la $a1,str      
    lb $a2,strLen       
    add $a1,$a1,$a2     
    add $t0,$a2,$zero   
    la $t3, strBuffer #here is the new buffer
    loop:
        subi $a1,$a1,1  
        beqz $t0,exit   
        subi $t0,$t0,1  
        lb $t4,0($a1)     # i load the string backwards byte by byte
        sb $t4,0($t3)     # i store it in the string buffer
        addi $t3,$t3,1     # i increment the memory adress of the buffer so that i can save the bytes one after the other
        j loop
exit:                      #I know my labels have to be changed but i will clean it later
la $a0,str_msg3           #print a leading message
li $v0,4
syscall
la $t8,strBuffer           #load the adress of the buffer and the string
la $t9, str
loop2:
    lb $t7,0($t8)          #load the first byte of the buffer
    beqz $t7,exit2         #check if its null 
    sb $t7,0($t9)          #store the byte in the strings adress at the first index
    addi $t8,$t8,1         #incrementing the adresses 
    addi $t9,$t9,1
    j loop2
exit2:                     #printing the result
la $a0,str
li $v0,4
syscall
li $v0,10
syscall

我已经呆了6个小时了,所以请原谅我缺乏适当的风格和凹痕。它似乎可以正常工作,并且从我可以告诉MARS数据段显示的内容中,字符串被逆转。

Cheers

哇,谢谢您的所有帮助!我在体面的睡眠中审查了我的代码,男孩看起来很混乱。阅读您的答复后,我重写了整个内容,并且有效:D(请参阅底部的代码)

我想对 @ped7g和@petercordes发表如此多的评论,告诉我我可以改进什么以及在与MIPS合作时应该考虑的事情。谢谢,我今天学到了一些东西。

.data 
str: .asciiz "ThE qUiCk BrOwN fOx JuMpS oVeR tHe LaZy DoG"
str_msg1: .asciiz "Original string: "
str_msg2: .asciiz "Reversed string: "
str_nl: .asciiz "n"
str_len: .word 0
.text
main:
#print original string
    la $a0,str_msg1     #leading text
    li $v0,4
    syscall
    la $a0,str              #original string
    li $v0,4
    syscall
    la $a0,str_nl           #new Line
    li $v0, 4
    syscall
#get lenght
    add $t0,$zero,$zero     #initialize registers when needed
    add $a0,$zero,$zero
    add $a1,$zero,$zero
    la $a0,str            #loads the adress of the string into two registers
    la $a1,str      
    getLen:
        lb $t0,0($a0)       #load first byte
        beqz $t0,saveLen  #check if byte is null and if so, goto saveLen
        addi $a0,$a0,1  #if not then increment the adress and keep going
        j getLen          #jump back to start of this loop
    saveLen:
        subu $t0,$a0,$a1  #len = adress of null terminator - str adress
        sw $t0,str_len
#reverse the string
    add $t0,$zero,$zero     #will hold the adress of the beginning of str
    add $t1,$zero,$zero     #will hold the adress of the end of str
    add $t2,$zero,$zero     #swap 1 
    add $t3,$zero,$zero     #swap 2 
    revString:
        #find the index of the last character before the end of the string
        la $t0,str             #loads the adress of the start of the string
        lw $t1,str_len         #loads the lenght of the string
        addu $t1,$t0,$t1      #now t1 is pointing to the null terminator
        subi $t1,$t1,1      #now t1 is pointing to the last character
        loop:
            lb $t2,0($t0)       #load the first character
            lb $t3,0($t1)       #load the last character
            ble $t1,$t0,printRev    #check to see when we reach the middle of the string
            sb $t3,0($t0)       #store the last letter at the beginning of the string
            sb $t2,0($t1)       #store the first letter at the end of the string
            addi $t0,$t0      #then increment/decrement the adress registers 
            subi $t1,$t1,1    #and loop until we reach the middle
            j loop
#print the reversed version of the text
    printRev:
    add $a0,$zero,$zero    #initialize the a0 registry
    la $a0,str_msg2      #leading text
    li $v0,4
    syscall
    la $a0,str          #reversed string
    li $v0,4
    syscall
    li $v0,10           #exit prorgram
    syscall

P.S。顺便说一句,我喜欢您如何在不反转的情况下打印出反向字符串,这表明您可能对正在发生的事情以及该计算机盒的工作方式具有牢固的基本掌握 ...

谢谢男人,很高兴阅读:)我现在对自己感觉好一些。不过,在对这样的网站提出问题之后,我总是想起我仍然不知道多少。

幸运的是,像您这样的人会花时间通过他们的经验和建议。尊敬的人!

反向反向算法建议:

Have two registers r1,r2 point to first/last character of string.
while (r1 < r2) {
    swap_chars_at_addresses(r1, r2);
    ++r1;
    --r2;
}

有关您的原始代码的一些评论:

strLen: .space 8

您为strLen保留8个字节,但是在代码中,您将其用作byte变量,它将您的代码仅限制为最大的127个字符长字符串(而字节可以为0..255范围,默认为lb确实会扩展该值,因此要达到完整的255限制,您必须将该字节视为无符号值)。

我强烈建议将其设置为:

strLen: .word 0

然后使用sw/lw将其视为32b签名的整数值,因为您具有可用的单词值,并且在指针算术中更有意义。另外,火星/SPIM的风险为零,有人会设法为您提供超过2 31 字符的输入字符串,并使长度值溢出到负数,因为虚拟MIPS机器上没有足够的内存在火星/Spim中,有这么大的字符串。在给您128个字符的长字符串并不困难时(那么您的旧代码会发疯)...

#Get the lenght of the string

它是拼写为"长度"的(我知道火星/Spim没有SpellChecker,但您的网络浏览器可能具有)。

la $a0,str
getLen:
    lb $t0,0($a0)
    beqz $t0,printLen
    addi $t1,$t1,1
    addi $a0,$a0,1
    j getLen

您没有初始化t1,因此您很幸运,火星/SPIM在执行代码之前将寄存器零零,并且它没有在syscall调用中修改它们。

但这仍然是脆弱的编程方式,而是初始化所有相关寄存器,并使相关指令保持紧凑(彼此接近)(即,在getLen之前初始化,在打印提示之前的代码启动后不立即初始化)。

另外,如果您要查看该代码,则有两次.. += 1;。那应该感觉像是不必要的冗余。确实,您可以避免:

la $a0,str
move $a1,$a0 # have str address also in a1
getLen:
    lb   $t0,0($a0)
    beqz $t0,getLenFinish
    addi $a0,$a0,1
    j getLen            # loop is one instruction shorter = may be faster
getLenFinish:
    subu $t0, $a0, $a1  # length = address of 0-terminator - str address
    sw   $t0,strLen     # store the calculated length into memory

现在从我的评论中设置那些" R1,R2"将很简单:

    # set t1 and t2 to point to first and last character of string
    la    $t1,str
    lw    $t2,strLen
    addu  $t2, $t1, $t2   #t2 = address of zero terminator
    subi  $t2, $t2, 1     #t2 = adr of last char (or out of bounds)
    # for empty string t2 is out of bounds now and shouldn't be dereferenced
    # And my algorithm was designed as "while (r1 < r2)" = false => no problem

这就是我停下来的地方,因为在...

上发表评论只是很有趣

P.S。顺便说一句,我喜欢您如何在不反转的情况下打印出反向的字符串,这表明您可能对正在发生的事情以及该计算机盒的工作方式具有坚实的基本掌握。许多其他MIPS问题感觉就像作者甚至没有想到"字符串"是存储在内存中的一系列字节值,并且您实际上可以向后读取它。

最新更新