汇编 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

指令总数可能会更长一些,因为您将有额外的 jaljr $ra 行,但它可以让您专注于更短和更简单的代码部分,同时 writing/debugging.