首页 > 编程知识 正文

strcmp比较中文字符串,strcmp函数比较

时间:2023-05-04 01:50:08 阅读:257427 作者:4915

要求写一个比较高效的文件比较程序,竟然发现memcmp比strcmp要快很多,于是跟踪调试,发现它们的实现原理:

intel/strcmp.asm:
    mov edx, dword ptr [esp + 4] ;取第二个参数地址
    mov ecx, dword ptr [esp + 8] ;取第一个参数地址
    test edx, 3  ;edx是第二个参数的地址,这里即检验该地址是否是4的倍数。
                         ;因为如果edx&3!=0,则其最低两位不为1,所以为4的倍数。这里有个内存地址对齐的问题。
    jne  dopartial ;如果地址不是4的倍数,就跳到dopartial去处理。
dodwords:
    mov  eax, dword ptr [edx]
    cmp  al, byte ptr [ecx]
    jne  donene
    or   al, al  ;看看字符串是否结束,这就是strcmp之所以比memcmp慢的地方了。
    je   doneeq  ;如果 al==0,则比较结束
    cmp  ah, byte ptr [ecx + 1]
    jne  donene
    or   ah, ah
    je   doneeq
    shr  eax, 10h ;右移16位
    cmp  al, byte ptr [ecx + 2]
    jne  donene
    or   al, al
    je   doneeq
    cmp  ah, byte ptr [ecx + 3]
    jne  donene
    or   ah, ah
    je   doneeq
    add  ecx, 4
    add  edx, 4
    or   ah, ah
    jne  dodwords
    move edi, edi ;这里一直大惑不解,不明白为什么这里要多出这两个字节来
doneeq:
    xor eax, eax   ;比较结果是相等,返回值为0
    ret
    nop ;这里也一直大惑不解,不明白这里为什么要插入一条空指令,感觉和上面的mov edi, edi应该是同一个原因。
donene:
    sbb eax, eax    ;比较结果不相等,这里也很经典,使用带借位减法,eax = eax - eax -cf。
    shl eax, 1        ;若不相等处是大于,则 cf == 0,eax == 0,下面加1后eax==1,返回。
    inc eax          ;若不相等处是小于,则 cf == 1,那么 sbb后eax==-1,补码为0xFFFFFFFF,左移再加1还是-1
    ret       
    mov edi, edi ;这里又出来了,不知道为什么要这么做,痛苦。
dopartial:
    test edx, 1
    je doword  ;同样,与1与如果为0,则地址是2的倍数,跳到doword去执行。
    mov al, byte ptr [edx]
    inc edx
    cmp al, byte ptr [ecx]
    jne donene
    inc ecx
    or al, al
    je doneeq
    test edx, 2
    je dodwords
dowords:
    mov ax, word ptr [edx]
    add edx, 2
    cmp al, byte ptr [ecx]
    jne donene
    or al, al
    je doneeq
    cmp ah, byte ptr[ecx + 1]
    jne donene
    or ah, ah
    je doneeq
    add ecx, 2
    jmp dodwords



intel/memcmp.asm
memcmp函数代码:
参数堆栈:
offset str1 --- ebp - 4 ------------当前栈顶
offset str2 --- ebp - 8
eax (strlen(str1)的返回值)
memcmp:
    mov eax, dword ptr [esp + 0ch] ;得到memcmp的第三个参数:要比较的个数
    test eax, eax ; 看要比较的字节数是否为0
    je retnull    ;如果要比较的字节数为0则直接返回
    mov edx, dword ptr [esp + 4] ;得到memcmp的第一个参数,即offset str1
    push esi
    push edi    ;保存寄存器值
    mov esi, edx    ;源字符串地址,即offset str1
    mov edi, dword ptr [esp + 10h] ;不知何意,
    or edx, edi
    and edx, 3    ;根据strcmp的分析,这里依然是判断edx地址是不是4的倍数
    je dwords    ;地址是4的倍数,则跳到dwords去处理
    test eax, 1    ;eax中存的是字符串长度。如果地址不是4的倍数,那么看要比较的字节数是不是2的倍数。
    je mainloop    ;如果要比较的内存字节数是2的倍数,则转向mainloop。
    mov cl, byte ptr [esi] ;否则eax==1,比较最后一个字节
    cmp cl, byte ptr [edi]
    jne not_equal 
    inc esi
    inc edi
    dec eax
    je done
main_loop:
    mov cl, byte ptr [esi]
    mov dl, byte ptr [edi]
    cmp cl, dl
    jne not_equal
    mov cl, byte ptr [esi+1]
    mov dl, byte ptr [edi+1]
    cmp cl, dl
    jne not_equal
    add edi, 2
    add esi, 2
    sub eax, 2
    jne main_loop ;这里用了jne而不是jmp,太好了,一举两得

done:   
    pop edi
    pop esi
retnull:
    ret
dwords:
    mov ecx, eax  ;eax中保存着字符串长度
    and eax, 3
    shr ecx, 2  ;右移两位,等于除以字符串长度除以4, 现在ecx == 100(64h)
    je tail_loop_start ;循环移位指令不影响除CF,OF以外的其它位,
            ;故这里是判断eax是否是4的倍数,若是(eax & 3 == 0, zf = 1)则跳
    repe cmps dword ptr [esi], dword ptr [edi]
        ;这是一条经典代码,cmps为串比较指令
        ;repe/repz:计数相等重复串操作指令功能:
        ; <1>如果cx==0或zf==0(比较的两数不等),则退出repe/repz
        ; <2>cx = cx - 1
        ; <3>执行其后的串指令
        ; <4>重复<1>--<3>
        ; 对于cmpsr的功能:
        ; 代码中是用dword ptr修饰过,所以是双字比较
        ; 比较完后:edi = edi +/- 4, esi = esi +/- 4
        ; 到底是加还是减,看df位的设置
    je tail_loop_start ;看repe是如何退出的,到底是全部比较完
               ;了都相等退出(则zf==1,je成功跳转),还是
               ;比较的中途遇到不相等的退出
    mov ecx, dword ptr [esi-4] ;已知是不相等退出的了,现在看具体的大小关系
    mov edx, dword ptr [edi-4] ;所以后退4个字节比较
    cmp cl, dl
    jne diffrence_in_tail
    cmp ch, dh
    jne diffrence_in_tail
    shr ecx, 10h
    shr edx, 10h
    cmp cl, dl
    jne diffrence_in_tail
    cmp ch, dh
diffrence_in_tail:
    mov eax, 0
not_equal:
    sbb eax, eax
    pop edi
    sbb eax, 0fffffffh
    pop esi
    ret
tail_loop_start:
    test eax, eax ;现在eax是原字符串长度模4后的零头
    je done    ;看看eax是否为0,是则说明比较完成,eax中是零,则返回值是0,相等
    mov edx, dword ptr [esi]
    mov ecx, dword ptr [edi]
    cmp dl, cl
    jne diffrence_in_tail
    dec eax
    je tail_done
    cmp dh, ch
    jne diffrence_in_tail
    dec eax
    je tail_done
    and ecx, 0ff0000h
    and edx, 0ff0000h
    cmp edx, ecx
    jne diffrence_in_tail
    dec eax
tail_doen:
    pop edi
    pop esi
    ret
  
至此,也明白了为什么这两个函数会有效率的差别,strcmp比较的字符串,而memcmp比较的是内存块,strcmp需要时刻检查是否遇到了字符串结束的 /0 字符,而memcmp则完全不用担心这个问题。另一个区别是
strcmp在比较四字节是逐字节比较,而memcmp是用了字符串比较指令,感觉用字符串比较指令比用逐字节比较好,不知道strcmp为什么比较四字节时不用。感觉memcmp倒是可以用来实现strncmp函数的功能。
遗留的问题有内存字节对齐的问题,以及两处mov edi,edi和一处nop指令的问题。交给以后吧。            

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。