向后读取一个字符串并保存它(string reversal with a twist)
Reading a string backwards and saving it(string reversal with a twist)
我是新来的,但我会尽力遵守指南。
请注意,我才刚刚开始使用 MiPS 和 MARS,所以如果我做了一些愚蠢的事情,请告诉我如何修复它,以便我可以改进。
我目前正在为我的长途装配课程做一项学校作业,我必须反转字符串。
问题是我无法创建新字符串,而是必须修改现有字符串。
我想我为什么要尝试在字符串中找到第一个+n/last-n 个字符,而我只能从字符串中的最后一个字符向后移动到字符串的第一个字符。
我设法向后读取并打印字符串,但现在我被卡住了,真的可以在正确的方向上使用微调。
如果你 运行 这段代码然后字符串 "dlroW olleH" 将被打印并且程序将退出,到目前为止一切顺利(我希望)。 我的问题是:
如何将字符串以反向格式保存到原始标签"str"?
我开始编写一些代码,但尚未完成,请参阅 "reverse" 标签以了解与我的问题相关的部分。到目前为止,这是我的代码:
请注意,我不能使用任何库,可以这么说,我必须手动执行此操作。
查看下面的编辑
.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
感谢您花时间阅读我的问题。
干杯!
编辑
所以我可能已经找到了答案,我想我应该 post 它以便其他人可以阅读它,也许它可以得到改进。我在技术上避开了这项任务允许的内容,但最后原始字符串被更改为反向格式,所以我认为它应该没问题。我通过创建缓冲区来做到这一点
(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 数据段显示的了解来看,字符串被颠倒了。
干杯
反向原位算法建议:
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 有符号整数值,因为您有可用的字值,这在指针运算中更有意义。此外,MARS/SPIM 中有人会设法为您提供超过 231 个字符的输入字符串并使长度值溢出为负数的风险为零,因为内存不足在 MARS/SPIM 的虚拟 MIPS 机器上有这么大的字符串。虽然给你 128 个字符长的字符串并不难(然后你的旧代码会发疯)...
#Get the lenght of the string
拼写为 "length"(我知道 MARS/SPIM 没有拼写检查器,但您的网络浏览器可能有)。
la $a0,str
getLen:
lb $t0,0($a0)
beqz $t0,printLen
addi $t1,$t1,1
addi $a0,$a0,1
j getLen
您没有初始化 t1
,所以您很幸运 MARS/SPIM 在执行您的代码之前确实将寄存器清零并且没有在 syscall
中修改它们来电。
但这仍然是一种脆弱的编程方式,而是初始化所有相关寄存器并使相关指令尽可能紧凑(彼此靠近)(即在 [=23= 之前初始化,而不是在开始之后立即初始化)打印提示前的代码)。
此外,如果您看一下该代码,就会发现 .. += 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 问题感觉作者甚至不知道 "string" 是存储在内存中的一系列字节值,而且您实际上可以向后读取它。
哇,感谢大家的大力帮助!在晚上睡得很好之后,我检查了我的代码,天哪,它看起来很乱。在阅读了您的回复后,我重写了整个内容并且它有效: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. BTW, I like how you managed to print out the reversed string without reversing it, that shows you have probably solid basic grasp of what is going on and how that computer box works ...
谢谢,伙计,读起来真好 :) 我现在对自己感觉好多了。尽管如此,在像这样的网站上提问后,我总是想起我还有多少不知道。
幸运的是,有些人像您一样愿意花时间分享他们的经验和建议。好人!
我是新来的,但我会尽力遵守指南。 请注意,我才刚刚开始使用 MiPS 和 MARS,所以如果我做了一些愚蠢的事情,请告诉我如何修复它,以便我可以改进。
我目前正在为我的长途装配课程做一项学校作业,我必须反转字符串。 问题是我无法创建新字符串,而是必须修改现有字符串。
我想我为什么要尝试在字符串中找到第一个+n/last-n 个字符,而我只能从字符串中的最后一个字符向后移动到字符串的第一个字符。 我设法向后读取并打印字符串,但现在我被卡住了,真的可以在正确的方向上使用微调。
如果你 运行 这段代码然后字符串 "dlroW olleH" 将被打印并且程序将退出,到目前为止一切顺利(我希望)。 我的问题是:
如何将字符串以反向格式保存到原始标签"str"?
我开始编写一些代码,但尚未完成,请参阅 "reverse" 标签以了解与我的问题相关的部分。到目前为止,这是我的代码: 请注意,我不能使用任何库,可以这么说,我必须手动执行此操作。
查看下面的编辑
.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
感谢您花时间阅读我的问题。 干杯!
编辑
所以我可能已经找到了答案,我想我应该 post 它以便其他人可以阅读它,也许它可以得到改进。我在技术上避开了这项任务允许的内容,但最后原始字符串被更改为反向格式,所以我认为它应该没问题。我通过创建缓冲区来做到这一点 (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 数据段显示的了解来看,字符串被颠倒了。
干杯
反向原位算法建议:
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 有符号整数值,因为您有可用的字值,这在指针运算中更有意义。此外,MARS/SPIM 中有人会设法为您提供超过 231 个字符的输入字符串并使长度值溢出为负数的风险为零,因为内存不足在 MARS/SPIM 的虚拟 MIPS 机器上有这么大的字符串。虽然给你 128 个字符长的字符串并不难(然后你的旧代码会发疯)...
#Get the lenght of the string
拼写为 "length"(我知道 MARS/SPIM 没有拼写检查器,但您的网络浏览器可能有)。
la $a0,str
getLen:
lb $t0,0($a0)
beqz $t0,printLen
addi $t1,$t1,1
addi $a0,$a0,1
j getLen
您没有初始化 t1
,所以您很幸运 MARS/SPIM 在执行您的代码之前确实将寄存器清零并且没有在 syscall
中修改它们来电。
但这仍然是一种脆弱的编程方式,而是初始化所有相关寄存器并使相关指令尽可能紧凑(彼此靠近)(即在 [=23= 之前初始化,而不是在开始之后立即初始化)打印提示前的代码)。
此外,如果您看一下该代码,就会发现 .. += 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 问题感觉作者甚至不知道 "string" 是存储在内存中的一系列字节值,而且您实际上可以向后读取它。
哇,感谢大家的大力帮助!在晚上睡得很好之后,我检查了我的代码,天哪,它看起来很乱。在阅读了您的回复后,我重写了整个内容并且它有效: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. BTW, I like how you managed to print out the reversed string without reversing it, that shows you have probably solid basic grasp of what is going on and how that computer box works ...
谢谢,伙计,读起来真好 :) 我现在对自己感觉好多了。尽管如此,在像这样的网站上提问后,我总是想起我还有多少不知道。
幸运的是,有些人像您一样愿意花时间分享他们的经验和建议。好人!