[英]Assembly x86: comparing strings doesn't work
我正在嘗試在 Assembly 中制作一個程序來檢查兩個字符串。
section .data
str1 db 'mystring'
str2 db 'mystring'
output db 'cmp went fine'
len equ $-output
section .text
global main
main:
mov ecx, str1
cmp ecx, str2
je ifBody0
int 80h
mov eax, 1
mov ebx, 0
int 80h
ifBody0:
mov eax, 4
mov ebx, 1
mov ecx, output
mov edx, outputlen
int 80h
奇怪的是,當我調用條件跳轉: je [label]
,它不起作用。 但是當我將je
更改為jne
,它起作用了。 我想知道我在這里做錯了什么。
為了在 x86-assembly 中比較字符串,有一個名為CMPS
(Compare Strings)的特殊操作碼。 在您的 BYTE 字符串的情況下,相關的操作碼是CMPSB
。 您可以通過將ESI
設置為源字符串並將EDI
設置為目標字符串來使用它。 相等檢查的長度(最好是最長的字符串)在ECX
設置。 小心溢出! .
所以你的代碼可能是這樣的:
section .data
str1 db 'mystring',0
str1len equ $-str1
str2 db 'mystring',0
output db 'cmp went fine',0x0a,0
outputlen equ $-output
output2 db 'cmp went wrong',0x0a,0
output2len equ $-output2
section .text
global main
main:
lea esi, [str1]
lea edi, [str2]
mov ecx, str1len ; selects the length of the first string as maximum for comparison
rep cmpsb ; comparison of ECX number of bytes
mov eax, 4 ; does not modify flags
mov ebx, 1 ; does not modify flags
jne ifWrong ; checks ZERO flag
ifRight: ; the two strings do match
mov ecx, output
mov edx, outputlen
int 80h
jmp exit
ifWrong: ; the two strings don't match
mov ecx, output2
mov edx, output2len
int 80h
exit: ; sane shutdown
mov eax, 1
mov ebx, 0
int 80h
讓我們從這兩個開始:
str1 db 'mystring'
mov ecx,str1
用匯編器編譯后,機器碼的原始字節看起來像這樣(這將在加載可執行文件后成為內存內容):
6D 79 73 74 72 69 6E 67 mystring
B9 00 00 00 00 ¹....
最后 4 個零是來自 'mystring' 的 'm' 字節的地址,因為我決定它將在地址 0 處編譯。前 8 個字節是字符串數據(ASCII 編碼), B9
是mov ecx,imm32
指令操作碼。
您不能將 string 放入ecx
, ecx
是 32 位寬(4 個字節),而 string 可以有很多字節。 因此,使用ecx
您最多可以從字符串中獲取 4 個字節,但這需要mov ecx,DWORD [str1]
,這會將值0x7473796D
放入ecx
(x86 是小端,因此第一個字節6D
在 DWORD 中是最不重要的( 32b) 值)。
但是mov ecx,str1
使用str1
符號加載ecx
,這是第一個'm'
字節( 0x00000000
)的地址。
要比較兩個字符串,您將兩個地址都加載到一些寄存器中,然后從這些地址加載字節,並一個一個地比較它們,直到找到一些差異(或字符串的結尾)(有更快的算法,但它們更復雜)並要求您知道前面字符串的長度,而逐字節比較可以輕松地處理類似 C 的零終止字符串)。
談到字符串的長度,你應該以某種方式定義一個。 在 C 中,通常在字符串的最后一個字符之后放置零(在該示例中將在B9
之前),在 C++ 中std::string
是將長度作為直接獲取/比較值的結構。 或者您可以在源代碼中對其進行硬編碼,例如您的outputlen
。
當您在匯編程序中編程時,您應該始終知道您正在處理多少位,並選擇正確的寄存器大小(或擴展值)和正確的內存緩沖區大小,以處理所需的值。
對於字符串,這意味着您必須決定字符串的編碼。 ASCII 是每個字符 8 位(1 個字節),UTF-8 每個字形的字節數可變,早期版本的 UTF-16 (UCS-2) 每個字形有 2 個字節(如 Java,但當前的 Utf-16 是可變長度的),Utf-32 固定為每個字形 4 個字節。 所以用 ASCII 編碼的字符串來獲取它的第一個字符意味着做mov al,BYTE [str1]
(或mov ecx,str1
mov al,[ecx]
-> al = 6Dh = 'm'
)用 Utf-32 來獲取第二個字符你必須做mov eax,DWORD [utf32str + 4]
。 對於 Utf-8,單個字符最多可以有 1 到 6 個字節的 IIRC,因此您必須以相當復雜的方式處理它,以識別有效的 utf-8 代碼並讀取正確的字節數。 但是如果您只想知道兩個 utf-8 字符串是否位相等,您可以逐字節比較它們,而無需自己處理字形。
當然,您應該了解寄存器的大小以及在 x86 上如何尋址某些寄存器的子部分的方式,即。 就像ax
部分(下 16b)從整個eax
(32b) 中取出,或者如何ah
: al
(high 8b : low 8b) 一起形成ax
。
我希望您在此之后會明白,您確實比較了兩個指針( str1
與str2
),它們總是不相等的,因為它們指向內存中的不同字節。 而不是比較內存中的內容(字符串)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.