【问题标题】:Compare user-inputted string/character to another string/character将用户输入的字符串/字符与另一个字符串/字符进行比较
【发布时间】:2017-09-23 07:36:40
【问题描述】:

所以我是 ARM 汇编的初学者(通常也是汇编)。现在我正在编写一个程序,其中最大的一部分是用户需要输入一个字母,然后我会将那个字母与其他一些预先输入的字母进行比较,看看用户是否输入了相同的字母事物。

例如,在我的代码中

.balign 4 /* Forces the next data declaration to be on a 4 byte segment */
dime: .asciz "D\n"

在文件的顶部和

addr_dime               : .word dime

在文件的底部。

另外,根据我在网上阅读的内容,我放了

.balign 4
inputChoice: .asciz "%d"

在文件的顶部,然后放

inputVal                : .word 0

在文件的底部。

在文件中间附近(相信我,这个独立代码有问题,文件的其余部分在这种情况下无关紧要)我有这个代码块:

ldr r3, addr_dime
ldr r2, addr_inputChoice
cmp r2, r3                  /*See if the user entered D*/
addeq r5, r5, #10           /*add 10 to the total if so*/

我认为应该将“D”加载到 r3 中,将用户输入的任何字符串或字符加载到 r2 中,如果它们相同,则将 10 加到 r5 中。

由于某种原因,这不起作用,并且 r5、r5、#10 代码只有在 addne 出现在它之前时才有效。

【问题讨论】:

    标签: string assembly arm string-comparison


    【解决方案1】:

    addr_dime : .word dime 过于复杂了。该地址已经是链接时间常数。将地址存储在内存中(在另一个有自己地址的位置)根本没有帮助,它只是增加了另一层间接。 (这实际上是您问题的根源。)

    无论如何,cmp 不会取消引用它的寄存器操作数,所以你是在比较指针。如果您使用调试器单步执行,您会看到寄存器中的值是指针。

    要加载dime 处的单个字节,零扩展到 r3,请执行

    ldrb r3, dime
    

    使用 ldr 进行 32 位加载也会得到 \n 字节,并且 32 位比较也必须匹配 eq 才能为真。

    但这只有在dime 足够接近以适应 PC 相对寻址模式时才有效;与大多数 RISC 机器一样,ARM 不能使用任意绝对地址,因为指令宽度是固定的。

    对于常量,避免这种情况的最简单方法是首先将其存储在内存中。使用.equ dime, 'D'定义一个数值常量,然后就可以使用了

    cmp r2, dime      @ compare with immediate operand
    

    ldr r3, =dime 要求汇编程序为您将常量放入寄存器。你可以用地址做到这一点,所以你可以这样做

    ldr   r2, =inputVal     @ r2 = &inputVal
    ldrb  r2, [r2]          @ load first byte of inputVal
    

    这是处理从静态数据加载的通用方法,对于 PC 相对寻址模式来说可能太远了。

    您可以通过使用堆栈地址(sub sp, #16 / mov r5, sp 或其他东西)来避免这种情况。那么您已经在寄存器中获得了地址。


    这正是 C 编译器所做的:

    char dime[4] = "D\n";
    char input[4] = "xyz";
    
    int foo(int start) {
        if (dime[0] == input[0])
            start += 10;
        return start;
    }
    

    来自Godbolt compiler explorer上的ARM32 gcc6.3:

    foo:
            ldr     r3, .L4         @ load a pointer to the data section at dime / input
            ldrb    r2, [r3]
            ldrb    r3, [r3, #4]
            cmp     r2, r3
            addeq   r0, r0, #10
            bx      lr
    .L4:
            @ gcc greated this "literal pool" next to the code
            @ holding a pointer it can use to access the data section,
            @ wherever the linker ends up putting it.
            .word   .LANCHOR0
    
    .section .data
    .p2align  2
     @@@ These are in a different section, near each other.
     @@@ On Godbolt, click the .text button to see full assembler directives.
    
    .LANCHOR0:      @  actually defined with a .set directive, but same difference.
    dime:
            .ascii  "D\012\000"
    input:
            .ascii  "xyz\000"
    

    尝试将 C 更改为与文字字符而不是编译器无法优化为常量的全局字符进行比较,看看会得到什么。

    【讨论】:

    • 当我这样做时,我得到一个错误,说 internal_relocation(type: OFFSET_IMM) not fix up
    • @PCRevolt:啊,是的,我想知道汇编程序是否会神奇地处理使用可以访问数据的寻址模式。 ARM 使用固定宽度指令,它们没有空间容纳任意 32 位绝对地址。存在偏移量较小的 PC 相对寻址模式,因此 ARM 代码通常使用“文字池”从靠近实际代码的块中加载数据。看看 C 编译器做了什么:godbolt.org/g/xnRrNa。如果您只是将堆栈内存用于缓冲区而不是静态内存,则可以避免这种情况。因此,您只需通过指针和ldrb r3, [r5] 获取地址。
    • 但是在这种情况下 r5 会是什么
    • @PCRevolt:我在想mov r5, sp 在堆栈上保留一些空间,并将该指针传递给 scanf 或 read,作为用户输入的目的地。但是请参阅我的更新答案。
    • 如何在堆栈上保留空间?我对此很陌生。不确定什么是文字池。
    猜你喜欢
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-16
    • 2023-03-10
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多