【问题标题】:MIPS Strcpy printing out garbageMIPS Strcpy 打印出垃圾
【发布时间】:2017-07-21 06:16:26
【问题描述】:

这是我给定参数的代码;这就是我的代码最终打印出来的内容。

测试函数_strCopy

请输入字符串:test

你刚刚输入:测试

_strCopy 的结果: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

如果两个字符串相同,则您的 _strCopy 工作正常。

老实说,汇编让我最头疼,考虑到大部分代码已经为我完成,我假设这应该很容易,我只需要制作函数。任何帮助将不胜感激。

Edit1:删除不需要的行 Edit2:已解决,必须更改

    move $t0, $a0
    move $t1, $a1

进入

    move $t0, $a1
    move $t1, $a0

然后解决了它

# Arguments:
#   - $a0: An address of the first character of a source string
#   - $a1: An address of a buffer
# Return Value:
#   - None
_strCopy:
    move $t0, $a0
    move $t1, $a1
strCopy_loop:
    lbu $t3, 0($t1)  # load
    sb $t3, 0($t0)  # write
    addi $t0, $t0, 1
    addi $t1, $t1, 1
    beq $t3, $zero, __strCopy_end   # Return if we hit a null terminator
    j strCopy_loop
__strCopy_loop2:
    addi $t2, $t2, -1
    sb $zero, 0($t2)
__strCopy_end:
    sub $v0, $t0, $a0
    jr $ra

【问题讨论】:

  • 什么是a2? (add $t2, $a0, $a2 在这里使用,没有描述值是什么)。如果这是 SPIM/MARS 问题,请在调试器中运行它,在函数开头放置断点,打开带有内存(缓冲区 1 和/或缓冲区 2)的窗口,然后逐行遍历指令以查看特定寄存器发生了什么值,并记忆内容。
  • @Ped7g 猜测,$a2 是字节长度(即这是strncpy,因为代码实现了字节长度限制)
  • 为什么会有la 的说明?如果这是一个真正的函数,你想使用调用者传递的$a0$a1,所以我会删除la。否则,该功能看起来还不错。
  • @CraigEstey 果然,从代码中很明显,但 OP 没有指定它。无论如何,它看起来不错,直到您阅读详细信息,有些值以相反的方式使用等等......以及您对la 的评论,我什至错过了。无论如何,SO不是调试服务,明显的错误正在发生,OP应该学习使用调试器来查看为什么没有写入buffer2。
  • a2 和 t2 的东西是剩下的;我刚刚删除了它们。还删除了 la 说明。虽然结果是一样的,但不知道过去该做什么。测试器从 buffer2 打印 strcopy 的结果。所以我想将地址a0 buffer1的字符串中的每个字符保存到buffer2中

标签: assembly mips strcpy


【解决方案1】:

另一种可能的实现方式:

.globl my_strcpy
.ent my_strcpy
my_strcpy:
    // Arguments:
    // a0: new str
    // a1: source str
    // a2: number of chars to copy

    li $t0, 0                // New str
    li $t1, 0                // Source str
    li $t2, 0                // Char to copy
    li $t3, 0                // Chars copied

    for:
        addu $t1, $a1, $t3     // Point to next char of source str
        lb $t2, 0($t1)         // Get next char of source str

        addu $t0, $a0, $t3     // Point to next char of new str
        sb $t2, 0($t0)         // Save next char of new str

        addi $t3, $t3, 1       // +1 chars copied   
        bne $a2, $t3, for      // Continue if there are more chars 

    jr $ra                     // Back to caller

.end my_strcpy

可以在 C 脚本上进行测试,如下所示:

#include <stdio.h>

extern void my_strcpy( char* d, char* s, size_t n );

void check( char* a, char* b, size_t n ){
    for( size_t i = 0; i < n; i += 1 ){
        if( a[i] != b[i] ){
            printf( "NOT OK \n" );
            return;
        }
    } 
    printf( "OK \n" );
}


int main(){
    char s[14] = "String sample"; // null terminator
    char d[14];
    my_strcpy( d, s, 14 );
    check( d, s, 14 );

    return 0;
}

【讨论】:

  • li $t0, 0$t1 是多余的:循环中第一次使用这些寄存器是只写的。此外,这是 memcpy 而不是 strncpy;它不会在字符串中的 0 字节处停止,总是复制请求的长度。
  • 另外,您可以通过在循环之前计算结束指针来将指令保存在循环内。然后循环分支可能只是bne src, end_src, for,并且您增加指针而不是循环内的索引。喜欢 C ends = s + len; / do{ *d++ = *s++; }while(s!=ends);。允许函数修改其$a0..3 寄存器。
  • 另外,如果这是一个真正的 MIPS,你可以在其中运行 C(不是 MARS 或 SPIM 模拟器),它通常会有分支延迟槽。因此,您可以使用 addu 填充它以后递增目标指针,例如 ... / sb / bne / addiu dst, dst, 1