鉴于变量:
var1 db "abcdefg", NULL
我如何执行循环来浏览每个字母?在C ++中,你会在循环中执行类似var [x]的操作,然后每次增加x。有什么想法吗?
答案 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]
)。
几乎可以肯定有一种更好(更聪明)的做法,而不是我在这里想到的,但你应该明白这一点。