字符串的比較與搜索

cmps指令用於比較字符串值,cmps指令有三種格式:cmpsb、cmpsw、cmpsl。隱含的源操作數和目標操作數位置存儲在esi和edi寄存器中,每次執行cmps指令時,根據DF標誌,esi和edi寄存器按照被比較的數據長度遞增或遞減。cmps指令從源字符串中減去目標字符串,並且適當地設置EFLAGS寄存器的進位、符號、溢出、零、奇偶校驗和富足進位標誌。cmps指令執行之後,可以根據字符串的值,使用一般的條件 跳轉指令跳轉到分支。

cmps指令和rep指令一起使用可以對跨越多個字節的字符串重複進行比較。但rep指令不在兩個重複的過程之間檢查標誌狀態,它只關心ecx寄存器中的計數值。所以使用rep指令中的其他指令:repe、repne、repz、repnz。這些指令在每次重複過程中檢查0標誌,如果0標誌被設置,就停止重複。rep其他指令使用如下表:

指令
描述
repe
等於時重複
repne
不等於時重複
repnz
不為零時重複
repz
為零時重複

示例:

#cmps.s
.section .data
val1:
    .ascii "Hello as!"
val2:
    .ascii "Hello wd!"

.section .text
.globl _start
_start:
    nop
    movl $1, %eax   #system call SYS_exit()
    leal val1, %esi
    leal val2, %edi
    movl $9, %ecx
    cld
    repe cmpsb
    je equal
    movl %ecx, %ebx
    int $0x80
equal:
    movl $0, %ebx
    int $0x80

該程序把源和目標字符串的位置加載到esi和edi寄存器中,字符串長度加載到ecx寄存器中,repe cmpsb指令逐字節地重複字符串的比較,直到ecx寄存器為0,或者0標誌被設置(說明不匹配)。 程序執行結果如下:

$ ./cmps
$ echo $?
2

ecx寄存器將包含不匹配字符在字符串中的位置,該位置是從字符串的末尾從0開始向回計數。 字符串的掃描使用scas指令,其提供了搜索一個字符或多個字符的方式。

scas指令類似其他字符串指令,有三種格式:scanb、scanw、scanl,三種格式分別比較內存中的一個字節和AL、AX、EAX寄存器的值。scas指令使用edi寄存器作為隱含的目標操作數。

edi寄存器必須包含要掃描的字符串的內存地址,當執行scas指令時,edi寄存器的值按照搜索字符數據長度遞增或遞減。

scas指令的一個非常有用的功能是確定0結尾的字符串導長度,對於0結尾的字符串,要搜索的顯然是0的位置,並且計算找到0經過 多少個字符。如下示例:

# scas.s
.section .data
string:
    .asciz "this is a test string!\n"
.section .text
.globl _start
_start:
    nop
    leal string, %edi         #將要用於查找的字符串的內存地址加載到edi寄存器中
    movl $0xffff, %ecx        #0xffff表明這個程序只能用於長度最大為65535的字符串
    movb $0, %al            #將要搜索的字符加載到al寄存器中
    cld
    repne scasb                    #使用repne指令掃描字符串,獲得搜索位置
    jne notfound                    #如果沒找到,跳轉到notfound分支
    subw $0xffff, %cx                #如果找到了,那麼其距離字符串末尾的位置就存放在cx寄存器中,從cx寄存器的值中減去字符串的長度
    neg %cx                            #使用neg指令改變結果的值的符號
    dec %cx                            #因為該長度包含表示結尾的0,所以最終值必須減1才能顯示字符串的真正長度。
    movl $1, %eax
    movl %ecx, %ebx            #將計算結果存放在ebx寄存器中。
    int $0x80
notfound:
    movl $1, %eax
    movl $0, %ebx
    int $0x80

運行程序結果如下:

$ ./scas
$ echo $?
23

书籍推荐