我正在编写一个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
(这些是gcc
或clang
,对于其他编译器,你应该检查它的文档)。
它正在做ret = (res > 0)
, (res> 0)是布尔表达式,所以它是0
或1
。
你可能想要if ((ret = palin(argv[1], strlen(argv[1]))) > 0)
,这说明了为什么有时最好使用KISS并将这些内容分成两行。
其它错误:
jl loop0
:应该是jb
。ecx
和edx
是内存指针,因此是无符号的。如果您的数据将在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
。