【问题标题】:Palindrome function always reporting offset #1 for error回文函数总是报告偏移量 #1 的错误
【发布时间】:2016-11-07 01:55:53
【问题描述】:

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

如果字符串是回文,则该函数返回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 #%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 - 有人知道这里发生了什么吗?

【问题讨论】:

  • 使用调试器单步调试您的代码。

标签: c string assembly x86 return-value


【解决方案1】:

您的代码中存在一些错误...您要查找的错误在这里:

if(ret = (palin(argv[1], strlen(argv[1])) &gt; 0))

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

它在做ret = (res &gt; 0),并且(res > 0)是布尔表达式,所以它是01

您可能想要if ((ret = palin(argv[1], strlen(argv[1]))) &gt; 0),这说明了为什么有时最好亲吻并将这些内容分成两行。


其他错误:

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

最后的风格 nitpick:jnz not_palindrome => 我会为这个使用 jne 别名,因为您正在比较两个字符是否相等,而不是“零”(这是相同的指令,只是别名不同,我倾向于两者都使用,尝试使用更适合遵循我对功能的“人性化”描述)。

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

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

【讨论】:

  • 另一种描述为人类读者选择合适的 JCC 同义词的方式是说你应该选择具有正确语义的同义词。
猜你喜欢
  • 2015-11-11
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多