回文函数总是报告错误#1的偏差

时间:2016-11-07 01:55:53

标签: c string assembly x86 return-value

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

如果字符串是回文,这个函数意味着返回0,如果字符串不是回文,它将返回失败的比较(即字符串左半部分的字符索引没有' t匹配)。

虽然它成功地检测了哪些字符串是否是回文,但它总是报告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 #%d\n",  argv[1], ret);    
    }
    else
    {
        printf("%s is a palindrome\n", 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 - 有谁知道这里发生了什么?

1 个答案:

答案 0 :(得分:2)

您的代码中存在很少的错误......您正在寻找的是:

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

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

它正在执行ret = (res > 0),而(res&gt; 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 =&gt;我会为这个使用jne别名,因为你要比较两个字符是否相等,而不是&#34;零&#34; (它是相同的指令,只是不同的别名,我倾向于使用两者,试图使用更合适的方式来遵循我的&#34;人类&#34;功能描述。)

此外,您可以执行cmp al,[edx]而无需将第二个字符加载到bl中(另外保存1条指令而不是ebx,因此您不需要push/pop ebx然后,再保存2个。)

如果您坚持将第二个字符加载到注册表中,那么“#34;易于阅读&#34;代码,你仍然可以使用ah作为第二个字符,从代码中完全删除ebx