汇编 MIPS:检查字符串是否为回文
Assembly MIPS: Checking if a string is palindromic or not
回文是可以双向读取的字符串。像 "radar"、"wow" 等
从 C 中我们知道我们可以使用 "for" 循环和 "if" 表达式检查给定的字符串:
for(i=0; i<length; i++)
if(str[i] == str[length-i-1])
printf("Palindromic");
else
print("Non Palindromic");
这样我们就可以从两边到达弦的中心。
这段代码需要我们统计字符数才能知道字符串的长度。
在 MIPS 上,这个 "for" 循环看起来相当复杂。
这是我自己的位置:
.data
str: .space 20
isntpal: .asciiz "it is not palindromic!"
ispal: .asciiz "it is palindromic!"
.text
.globl main
main:
add $t0, $zero, $zero #t0 = i counter for the loops
add $t1, $zero, $zero #t1 = j counter for length of the string
li $v0, 8 #gets(str)
la $a0, str
la $a1, 20
syscall
length:
lb $s0, str($t0) #load each character to s0
beq $s0, '[=11=]', NEXTCHECK
add $t0, $t0, 1 #i++ to scan all the characters of the string
add $t1, $t1, 1 #j++ for the total length of the string
j length
NEXTCHECK:
add $t0, $zero, $zero #clean the t0 register from the length loop
pal:
sub $t4, $t1, $t0 #length - i - 1
sub $t4, $t4, 1
lb $s0, str($t0) #str[i]
lb $s1, str($t4) #str[length-i-1]
slt $t3, $t0, $t1 #for(i=0; i<length; i++)
beq $t3, $zero, EXIT
add $t0, $t0, 1 #i++
beq $s0, $s1, ELSE #if (str[i] == str[length-i-1])
li $v0, 4 #printf("It is palindromic");
la $a0, ispal
syscall
j EXIT
ELSE:
li $v0, 4 #else printf("It is not palindromic");
la $a0, isntpal
syscall
j EXIT
j pal
EXIT:
li $v0, 10
syscall
我无法理解我应该在哪里放置 EXIT 和 ELSE 标签,我认为这就是为什么总是 returns 字符串是回文的原因,即使它不是。
标签的正确放置方式是什么?
是否需要多个标签?
正确高效的 C 版本:
bool is_palindrome(const char * str, const size_t length) {
const char * frontptr = str; // front pointer: points at the very first character of string
const char * backptr = str + length-1; // back pointer: points at the very last character of string
while (frontptr < backptr) { // while front pointer points ahead of back pointer
if (*frontptr != *backptr) return false; // characters differ => not a palindrome
++frontptr; // move front pointer at next character in string
--backptr; // move back pointer at "next" character toward start of string
}
// front pointer points at/beyond back pointer
// all chars were compared (except middle one for odd length string, which is "palindromic" always)
// and all were equal, thus the input string is a palindrome, if this point is reached
return true;
}
这段 C 代码是有意编写的,应该可以非常直接地转换为 ASM(例如每行 1-2 条指令)。
如果知道地址(C 中的指针),如何从内存中加载值:
la $a0,some_address # a0 = address (pointer)
lb $t0,($a0) # t0 = byte loaded from memory at a0
如何在 ASM 中 increment/decrement 指针:将指向的元素的大小添加到指针的当前值。
对于 ASCII 字符串,元素是单字节,因此要移动一个字符 forth/back,您必须向指针添加 +1/-1,例如:
addi $a0,$a0,1 # ++byte_ptr
addi $a1,$a1,-1 # --byte_ptr
如果你要使用字数组,你会想做 +-4 将指针移动一个元素 forth/back。
并学习如何使用 "procedures" in MIPS ASM,这样你就可以创建一些通用的通用 "get string length" 代码,然后稍后通过简单的 copy/paste 重新使用它(除非你最终创建一些你自己的图书馆)。
如果您遵循 C++ 示例,回文测试也可以作为单独的过程完成。
总的来说,您需要维护一些更简单的代码 + 调试 + 原因如下:
# input string
# prepare arguments for get_length
# call get_length
# process result and prepare arguments for is_palindrome
# call is_palindrome
# set a0 to one of the two result strings based on return value
# display string
# exit
指令总数可能会更长一些,因为您将有额外的 jal
和 jr $ra
行,但它可以让您专注于更短和更简单的代码部分,同时 writing/debugging.
回文是可以双向读取的字符串。像 "radar"、"wow" 等
从 C 中我们知道我们可以使用 "for" 循环和 "if" 表达式检查给定的字符串:
for(i=0; i<length; i++)
if(str[i] == str[length-i-1])
printf("Palindromic");
else
print("Non Palindromic");
这样我们就可以从两边到达弦的中心。
这段代码需要我们统计字符数才能知道字符串的长度。
在 MIPS 上,这个 "for" 循环看起来相当复杂。
这是我自己的位置:
.data
str: .space 20
isntpal: .asciiz "it is not palindromic!"
ispal: .asciiz "it is palindromic!"
.text
.globl main
main:
add $t0, $zero, $zero #t0 = i counter for the loops
add $t1, $zero, $zero #t1 = j counter for length of the string
li $v0, 8 #gets(str)
la $a0, str
la $a1, 20
syscall
length:
lb $s0, str($t0) #load each character to s0
beq $s0, '[=11=]', NEXTCHECK
add $t0, $t0, 1 #i++ to scan all the characters of the string
add $t1, $t1, 1 #j++ for the total length of the string
j length
NEXTCHECK:
add $t0, $zero, $zero #clean the t0 register from the length loop
pal:
sub $t4, $t1, $t0 #length - i - 1
sub $t4, $t4, 1
lb $s0, str($t0) #str[i]
lb $s1, str($t4) #str[length-i-1]
slt $t3, $t0, $t1 #for(i=0; i<length; i++)
beq $t3, $zero, EXIT
add $t0, $t0, 1 #i++
beq $s0, $s1, ELSE #if (str[i] == str[length-i-1])
li $v0, 4 #printf("It is palindromic");
la $a0, ispal
syscall
j EXIT
ELSE:
li $v0, 4 #else printf("It is not palindromic");
la $a0, isntpal
syscall
j EXIT
j pal
EXIT:
li $v0, 10
syscall
我无法理解我应该在哪里放置 EXIT 和 ELSE 标签,我认为这就是为什么总是 returns 字符串是回文的原因,即使它不是。
标签的正确放置方式是什么?
是否需要多个标签?
正确高效的 C 版本:
bool is_palindrome(const char * str, const size_t length) {
const char * frontptr = str; // front pointer: points at the very first character of string
const char * backptr = str + length-1; // back pointer: points at the very last character of string
while (frontptr < backptr) { // while front pointer points ahead of back pointer
if (*frontptr != *backptr) return false; // characters differ => not a palindrome
++frontptr; // move front pointer at next character in string
--backptr; // move back pointer at "next" character toward start of string
}
// front pointer points at/beyond back pointer
// all chars were compared (except middle one for odd length string, which is "palindromic" always)
// and all were equal, thus the input string is a palindrome, if this point is reached
return true;
}
这段 C 代码是有意编写的,应该可以非常直接地转换为 ASM(例如每行 1-2 条指令)。
如果知道地址(C 中的指针),如何从内存中加载值:
la $a0,some_address # a0 = address (pointer)
lb $t0,($a0) # t0 = byte loaded from memory at a0
如何在 ASM 中 increment/decrement 指针:将指向的元素的大小添加到指针的当前值。
对于 ASCII 字符串,元素是单字节,因此要移动一个字符 forth/back,您必须向指针添加 +1/-1,例如:
addi $a0,$a0,1 # ++byte_ptr
addi $a1,$a1,-1 # --byte_ptr
如果你要使用字数组,你会想做 +-4 将指针移动一个元素 forth/back。
并学习如何使用 "procedures" in MIPS ASM,这样你就可以创建一些通用的通用 "get string length" 代码,然后稍后通过简单的 copy/paste 重新使用它(除非你最终创建一些你自己的图书馆)。
如果您遵循 C++ 示例,回文测试也可以作为单独的过程完成。
总的来说,您需要维护一些更简单的代码 + 调试 + 原因如下:
# input string
# prepare arguments for get_length
# call get_length
# process result and prepare arguments for is_palindrome
# call is_palindrome
# set a0 to one of the two result strings based on return value
# display string
# exit
指令总数可能会更长一些,因为您将有额外的 jal
和 jr $ra
行,但它可以让您专注于更短和更简单的代码部分,同时 writing/debugging.