我是新来的,但我会尽力遵循准则。请注意,我只是从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问题感觉就像作者甚至没有想到"字符串"是存储在内存中的一系列字节值,并且您实际上可以向后读取它。