如何索引汇编中的字符串

Rob*_*ert 5 arrays string x86 assembly c-strings

鉴于变量:

var1    db  "abcdefg", NULL
Run Code Online (Sandbox Code Playgroud)

我将如何执行循环来导航每个字母?在 C++ 中,您会在循环内执行类似 var[x] 的操作,然后每次都增加 x。有任何想法吗?

Cod*_*ray 5

在 C 和 C++ 中,字符串以 NUL 结尾。这意味着将 ASCII NUL 字符 (0) 添加到字符串的末尾,以便代码可以判断字符串的结尾位置。该strlen函数从头开始遍历字符串,并保持循环,直到遇到该 NUL 字符。当它找到 NUL 时,它知道这是字符串的结尾,并且它返回从开头到 NUL 的字符数作为字符串的长度。

字符串文字(双引号中的内容)由 C/C++ 编译器自动以 NUL 结尾,因此:

"abcdefg"
Run Code Online (Sandbox Code Playgroud)

等价于下面的数组:

{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0}
Run Code Online (Sandbox Code Playgroud)

我提到这一点是因为 Peter Rader 在他的回答中提出了这一点,而你并没有真正理解他在说什么。但是,您似乎已经知道这一点,因为您在程序集声明中的字符串中附加了一个 NUL 字符:

var1    db  "abcdefg", NULL
Run Code Online (Sandbox Code Playgroud)

NULL现在,通常我们不会为此使用标识符。尤其是在 C 中,NULL被定义为空指针。我们只使用文字 0,因此定义为:

var1    db  "abcdefg", 0
Run Code Online (Sandbox Code Playgroud)

但你的代码可能会工作,假设NULL在某个地方定义为 0。

所以你的设置全部正确。现在您需要做的就是编写循环:

    mov  edx, OFFSET var1    ; get starting address of string

Loop:
    mov  al, BYTE PTR [edx]  ; get next character
    inc  edx                 ; increment pointer
    test al, al              ; test value in AL and set flags
    jz   Finished            ; AL == 0, so exit the loop

    ; Otherwise, AL != 0, so we fell through.
    ; Here, you can do do something with the character in AL.
    ; ...

    jmp  Loop                ; keep looping

Finished:
Run Code Online (Sandbox Code Playgroud)

您说您熟悉该CMP说明。在上面的代码中,我使用了TEST而不是CMP. 你可以等效地写:

cmp  al, 0
Run Code Online (Sandbox Code Playgroud)

test al, al
Run Code Online (Sandbox Code Playgroud)

效率稍高一些,因为它是一条较小的指令,所以我只是习惯在将寄存器的值与 0 进行比较的特殊情况下这样编写。编译器也会生成此代码,所以它很好熟悉它。


额外的闲聊:表示字符串的另一种方法是将其长度(以字符为单位)与字符串本身一起存储。这就是 Pascal 语言传统上所做的事情。这样,您就不需要在字符串末尾使用特殊的 NUL 标记字符。相反,该声明将如下所示:

var1    db  7, "abcdefg"
Run Code Online (Sandbox Code Playgroud)

其中每个字符串的第一个字节是它的长度。与 C 风格相比,这具有多种优点,即您不必遍历整个字符串来确定其长度。当然,主要缺点是字符串的长度限制为 255 个字符,因为这就是一个 BYTE 所能容纳的全部字符。

不管怎样,在预先知道长度的情况下,您不再检查 NUL 字符,您只需迭代与字符串中的字符相同的次数:

    mov  edx, OFFSET var1    ; get starting address of string
    mov  cl, BYTE PTR [edx]  ; get length of string

Loop:
    inc  edx                 ; increment pointer
    dec  cl                  ; decrement length
    mov  al, BYTE PTR [edx]  ; get next character
    jz   Finished            ; CL == 0, so exit the loop

    ; Do something with the character in AL.
    ; ...

    jmp  Loop                ; keep looping

Finished:
Run Code Online (Sandbox Code Playgroud)

(在上面的代码中,我假设所有字符串的长度至少为 1 个字符。这可能是一个安全的假设,并且避免了在循环上方进行长度检查的需要。)

或者,您可以执行您提到的数组索引,但如果您想向前迭代字符串,则必须小心一点:

    mov   edx, OFFSET var1        ; get starting address of string
    movzx ecx, BYTE PTR [edx]     ; get length of string
    lea   edx, [ecx+1]            ; increment pointer by 1 + number of chars
    neg   ecx                     ; negate the length counter
Loop:
    mov   al, BYTE PTR [edx+ecx]  ; get next character

    ; Do something with the character in AL.
    ; ...

    inc   ecx
    jnz   Loop                     ; CL != 0, so keep looping
Run Code Online (Sandbox Code Playgroud)

基本上,我们设置EDX为指向字符串的末尾ECX,将计数器 ( ) 设置为字符串长度的负数,然后通过索引读取字符[EDX+ECX](因为我们取反了ECX,所以相当于[EDX-ECX])。

几乎可以肯定,有一种比我在这里想到的更好(更聪明)的方法来做到这一点,但你应该明白这个想法。