汇编代码运行时检查失败:ESP值未正确保存问题排查
解决Run-Time Check Failure #0错误(ESP值未正确保存)
核心问题:Proc5函数的C++声明使用__fastcall调用约定,但汇编实现未遵循该约定的栈平衡、寄存器保存规则,导致ESP指针在调用后无法正确恢复,触发运行时检查错误。
__fastcall调用约定的关键规则
- 前两个DWORD参数通过
ECX、EDX寄存器传递,剩余参数从右往左依次入栈 - 被调用者负责清理栈上的剩余参数(通过
ret 数值指令) - 函数内使用
EBX、ESI、EDI、EBP等非易失性寄存器时,必须先保存,函数结束前恢复
你的代码中的具体问题
- 栈平衡错误:汇编末尾使用
ret而非带参数的ret。Proc5共6个参数,前两个用寄存器传递,剩余4个参数占16字节,被调用者需要通过ret 16清理栈,否则ESP无法回到调用前的正确位置。 - 非易失性寄存器未保存:汇编中直接使用了
EBX、ESI,但未在函数开头保存这些寄存器,违反调用约定,破坏调用者的寄存器状态。 - 参数获取位置错误:__fastcall调用时,栈上参数的布局是返回地址后依次为
raznost、n、N、result_5(从右往左入栈),你的代码中对栈参数的引用(如[esp + 12])全部错位,导致逻辑错误同时加剧栈不平衡。
修正后的汇编代码示例
@Proc5@24 proc ; 保存非易失性寄存器与栈帧 push ebp mov ebp, esp push ebx push esi push edi ; 按__fastcall规则获取参数 mov esi, ecx ; esi = arr(第一个参数,ECX传递) mov edi, edx ; edi = freq(第二个参数,EDX传递) mov ecx, [ebp + 8] ; ecx = raznost(第三个参数,栈顶第一个) mov edx, [ebp + 12] ; edx = n(第四个参数) mov ebx, [ebp + 16] ; ebx = N(第五个参数) mov eax, [ebp + 20] ; eax = result_5(第六个参数) ; 统计元素频率逻辑(修正寄存器与参数引用) imul ecx, 4 add edi, ecx ; 调整freq数组偏移,处理负数 xor ecx, ecx ; 数组索引计数器 loop_1: cmp ecx, edx je loopend_1 mov ecx, [esi + ecx*4] ; 获取arr[ecx] inc dword ptr [edi + ecx*4] ; 对应频率计数+1 inc ecx jmp loop_1 loopend_1: ; 按频率排序逻辑(此处需根据需求调整,确保参数引用正确) xor ecx, ecx ; 新数组元素计数器 outer_loop: cmp ecx, ebx ; 遍历所有可能的数值范围(N) je outer_loopend ; ... 此处插入你的排序逻辑,注意使用正确的寄存器/参数 ... outer_loopend: ; 恢复非易失性寄存器 pop edi pop esi pop ebx pop ebp ; 清理栈上4个参数(共16字节),平衡ESP ret 16 @Proc5@24 endp
额外验证步骤
- 确保C编译选项与汇编的调用约定一致(如VC中默认
__cdecl,需显式指定__fastcall) - 调试时查看栈帧布局,确认每个参数的内存位置与代码中的引用匹配
- 运行前检查所有非易失性寄存器的保存与恢复逻辑
内容的提问来源于stack exchange,提问作者Zarzd
相关产品推荐
相关产品推荐

