C语言 回文函数总是报告错误偏移量#1



我正在编写一个x86汇编函数,用于确定字符串是否为回文(除null终止符外)。

如果字符串是回文,该函数将返回0,如果字符串不是回文,它将返回失败的比较(即字符串左半部分不匹配的字符的索引)。

当它成功地检测到哪些字符串是回文,哪些不是回文时,它总是报告1作为失败的回文测试的索引,而不管它实际上在哪里失败。

汇编代码:

.386
.MODEL FLAT, C
.CODE
    ; Determines whether or not a given string is a palindrome
    ; Uses:
    ;   ECX - pointer to start of string (incremented till halfway)
    ;   EDX - pointer to end of string (decremented till halfway)
    ;   AL - dereference character from ECX for comparison
    ;   BL - dereference character from EDX for comparison
    ;   ESI - index where comparison failed in case strings are not palindromes
    ; Arguments:
    ;   [ESP+4] - pointer to string to test
    ;   [ESP+8] - length of string
    ; Returns:
    ; 0 = string is a palindrome
    ; > 0 = string is not a palindrome; return value is the # comparison that failed (e.g. AABAAA would return 3)
    ; C prototype: int __cdecl palin(char *str, int len);
    palin PROC
        push ebx
        push esi
        ; Load ECX with a pointer to the first character in the string
        mov ecx, dword ptr [esp+12]
        ; Copy the pointer into EDX then add the length so EDX points to the end of the string
        mov edx, ecx
        add edx, dword ptr [esp+16]
        xor esi, esi
        loop0:
            ; Begin loop with decrement of EDX to skip the null terminator
            dec edx
            inc esi
            mov al, byte ptr [ecx]
            mov bl, byte ptr [edx]
            cmp al, bl
            ; Comparison fail = strings cannot be palindromes
            jnz not_palindrome
            inc ecx
            ; If start ptr >= end ptr we are done, else keep looping
            cmp ecx, edx 
        jl loop0
        ; Return 0 = success; string is a palindrome
        xor eax, eax
        jmp end_palin
        not_palindrome: 
        ; Return > 0 = fail; string is not a palindrome
            mov eax, esi
        end_palin:  
            pop esi
            pop ebx
            ret
    palin ENDP
END

汇编函数的C驱动:

#include <stdio.h>
#include <string.h>
int __cdecl palin(char *str, int len);
int __cdecl main(int argc, char *argv[])
{
    int ret;
    if(argc<2) 
    {
        printf("Usage: pal word");
        return 0;
    }
    if(ret = (palin(argv[1], strlen(argv[1])) > 0))
    {
        printf("%s is not a palindrome; first comparison that failed was #%dn",  argv[1], ret);    
    }
    else
    {
        printf("%s is a palindromen", argv[1]);
    }
    return 0;
}
样本输出:

C:Temp>pal ABCDEEDCBA
ABCDEEDCBA is a palindrome
C:Temp>pal ABCDEDCBA
ABCDEDCBA is a palindrome
C:Temp>pal AABAAA
AABAAA is not a palindrome; first comparison that failed was #1

最后一行应该返回3而不是1 -有人知道这里发生了什么吗?

你的代码中有几个bug…您要找的文件在这里:

if(ret = (palin(argv[1], strlen(argv[1])) > 0))

这个应该在好的C/c++编译器中发出警告,我想,你在用什么?你使用-Wall -Wextra(这些是gccclang,对于其他编译器,你应该检查它的文档)。

它正在做ret = (res > 0), (res> 0)是布尔表达式,所以它是01

你可能想要if ((ret = palin(argv[1], strlen(argv[1]))) > 0),这说明了为什么有时最好使用KISS并将这些内容分成两行。


其它错误:

jl loop0:应该是jbecxedx是内存指针,因此是无符号的。如果您的数据将在0x80000000边界上分配,那么jl将首先在cmp上失败。

你可以简化退出逻辑:

    ; Return 0 = success; string is a palindrome
        xor esi, esi    ; fake "esi" index = 0, reusing "not palindrome" exit code fully
    not_palindrome: 
    ; Return > 0 = fail; string is not a palindrome
        mov eax, esi
        pop esi
        pop ebx
        ret

和最后的风格挑剔:jnz not_palindrome =>我会使用jne别名为这一个,因为你比较两个字符相等,而不是"零"(这是相同的指令,只是不同的别名,我倾向于使用两者,试图使用更合适的遵循我的"人类"功能描述)。

也可以在不将第二个字符加载到bl的情况下执行cmp al,[edx](节省1条指令而不破坏ebx,因此您不需要push/pop ebx,节省2条指令)。

如果您坚持将第二个字符加载到寄存器中只是为了"易于阅读"的代码,您仍然可以使用ah作为第二个字符,完全从代码中删除ebx

最新更新