回文函数总是报告错误的偏移量#1
Palindrome function always reporting offset #1 for error
我正在编写一个 x86 汇编函数来确定一个字符串是否为回文(空终止符除外)。
这个函数的意思是 return 0 如果字符串是回文,如果字符串不是回文,它会 return 比较失败的(即字符在回文上的索引)留下不匹配的字符串的一半)。
虽然它成功地检测出哪些字符串是回文,哪些不是回文,但它总是报告 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
最后一行应该 return 3 而不是 1 - 有人知道这里发生了什么吗?
您的代码中几乎没有错误...您要找的错误就在这里:
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
别名,因为你正在比较两个字符是否相等,而不是 "zero" (它是相同的指令,只是别名不同,我倾向于使用两者,尝试使用更合适的来遵循我对功能的"human"描述。
您也可以执行 cmp al,[edx]
而无需将第二个字符加载到 bl
(节省 1 条指令而不破坏 ebx
,因此您不需要 push/pop ebx
然后,再节省 2 个)。
如果您坚持只为 "easy to read" 代码将第二个字符加载到寄存器中,您仍然可以对第二个字符使用 ah
,从代码中完全删除 ebx
。
我正在编写一个 x86 汇编函数来确定一个字符串是否为回文(空终止符除外)。
这个函数的意思是 return 0 如果字符串是回文,如果字符串不是回文,它会 return 比较失败的(即字符在回文上的索引)留下不匹配的字符串的一半)。
虽然它成功地检测出哪些字符串是回文,哪些不是回文,但它总是报告 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
最后一行应该 return 3 而不是 1 - 有人知道这里发生了什么吗?
您的代码中几乎没有错误...您要找的错误就在这里:
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
别名,因为你正在比较两个字符是否相等,而不是 "zero" (它是相同的指令,只是别名不同,我倾向于使用两者,尝试使用更合适的来遵循我对功能的"human"描述。
您也可以执行 cmp al,[edx]
而无需将第二个字符加载到 bl
(节省 1 条指令而不破坏 ebx
,因此您不需要 push/pop ebx
然后,再节省 2 个)。
如果您坚持只为 "easy to read" 代码将第二个字符加载到寄存器中,您仍然可以对第二个字符使用 ah
,从代码中完全删除 ebx
。