【问题标题】:How to index through a string in assembly如何通过汇编中的字符串进行索引
【发布时间】:2017-11-16 00:15:01
【问题描述】:

给定变量:

var1    db  "abcdefg", NULL

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

【问题讨论】:

    标签: arrays string assembly x86 c-strings


    【解决方案1】:

    在 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 个字符。这可能是一个安全的假设,并且避免了在循环。)

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

        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])。

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

    【讨论】:

    • 我相信问题更多是关于如何索引到数组中。所以我认为使用mov al, [var1 + edx] 的简单解决方案应该是合适的。
    • 好吧,也许吧。它确实说,“我将如何执行循环来导航每个字母?”,这就是我回答的部分。如果您只想获取其中一个字符,mov al, BYTE PTR [var1 + index](其中 index 是立即值或包含该值的寄存器)自然会起作用。不知道我应该让答案更长。 :-)
    • mov al,0 , rep scasb ?
    • 是的,rep scasb 可用于通过搜索终止 NUL 字符来查找字符串的长度。这是用汇编语言实现strlen 的一种(极其)低效的方法(除非您正在为 8088 编程)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    相关资源
    最近更新 更多