如何在程序集中索引字符串

时间:2017-06-14 03:00:18

标签: assembly

鉴于变量:

var1    db  "abcdefg", NULL

我如何执行循环来浏览每个字母?在C ++中,你会在循环中执行类似var [x]的操作,然后每次增加x。有什么想法吗?

1 个答案:

答案 0 :(得分:2)

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

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

"abcdefg"

相当于以下数组:

{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0}

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

var1    db  "abcdefg", NULL

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

var1    db  "abcdefg", 0

但是你的代码可能有效,假设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:

你说你熟悉CMP指令。在上面的代码中,我使用了TEST而不是CMP。你可以等同地写:

cmp  al, 0

test al, al

稍微有点效率,因为它是一个较小的指令,所以我只是习惯于在特殊情况下编写它,我将寄存器的值与0进行比较。编译器也将生成此代码,所以熟悉它是件好事。

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

var1    db  7, "abcdefg"

其中每个字符串的第一个字节是其长度。 This has various advantages over the C style,即您不必遍历整个字符串以确定其长度。当然,主要的缺点是字符串的长度限制为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:

(在上面的代码中,我假设所有字符串都是长度为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

基本上,我们将EDX设置为指向字符串的 end ,我们将计数器(ECX)设置为否定字符串的长度,然后我们通过索引[EDX+ECX]来读取字符(因为我们否定了ECX,它等同于[EDX-ECX])。

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