我正在研究一个需要内联汇编代码来反转字符串的c ++代码。 因此,如果我的输入是:“qwerasd”,则输出应为“dsarewq”。 我想过使用堆栈实现这个。我的代码是:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void reverseString(char* buffer, int len){
__asm {
push ecx
push edi
push eax
mov ecx, len
mov edi, buffer
top:
mov al, BYTE PTR [edi]
push al
inc edi
loop top
mov ecx, len
mov edi, buffer
refill:
pop al
mov BYTE PTR [edi], al
inc edi
loop refill
}
}
int main() {
char s[64];
char *ptr = s;
int size;
printf("\nEnter text: ");
scanf("%s", s);
size = strlen(s);
reverseString(ptr, size);
printf("\nThe new text is: %s\n\n", s);
exit(0);
}
我试图逐个将字符推入堆栈,然后逐个弹出它们并将其存回字符串中。
当我运行代码时,我收到以下错误: 运行时检查失败#0 - ESP的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数和使用不同调用约定声明的函数指针的结果。
我做错了什么?
答案 0 :(得分:3)
对于我来说,你想要用汇编语言来做这件事似乎有些荒谬,但无论如何。你的问题就在这里:
push ecx
push edi
push eax
你永远不会把那些从堆叠中弹出来!你放在堆叠上的所有东西,你必须在离开__asm { ... }
区块之前再次取消。
请注意,可能没有必要保存所有这些寄存器。我对此并不是百分之百确定,但我认为Windows ABI说eax
,ecx
和edx
是调用被破坏的,这意味着您不必在像这样的叶子程序中保存和恢复它们。
答案 1 :(得分:3)
除了Zack的回答之外,推送edi
实际上并不是必需的,因为看起来编译器会自动为您执行此操作,至少对于msvc而言。
上述代码的另一个问题是,您正在执行pop al
,只会将esp
增加一个字节。这显然会导致问题,因为正常的推送弹出操作预计在32位下进行4字节对齐。当你的函数退出时,它最终会为ebp
恢复错误的值,它会返回一些错误的返回地址并随之发生混乱。
您可以这样修复:
top:
movzx eax, BYTE PTR [edi]
push eax
// ...
refill:
pop eax
mov BYTE PTR [edi], al
修改:为了添加一些说明,al
不是push
pop
上有效的操作数大小。不知道为什么msvc没有给出错误。上面的程序集最终会被转换为push eax
和pop ax
,而不是来自不平衡的地方。