Visual Studio内联汇编中递归函数为何直接执行完毕跳到末尾?
我正在编写的一段x86汇编代码如下,是在Visual Studio中通过_asm内联到C/C++代码中的:
HPSTART: CMP EBX, 1 // if n = 1 JNE HPERMUT MOV EDX, N INSERT: MOV ECX, Perm[4320 + EDX * 4] MOV Perm[EDI * 4], ECX INC EDI DEC EDX TEST EDX, EDX JNZ INSERT RET HPERMUT: XOR ESI, ESI // else, i = 0 HLOOP: CMP ESI, EBX // i < N, 对应逻辑 for (i = 0; i < n; i++) JE ITEREND PUSH ESI DEC EBX CALL HPSTART
EBX寄存器初始值为6,最大值为6。我原本预期单步越过CALL HPSTART行时,指令指针会跳回HPSTART执行完整逻辑,直到最后一次递归调用EBX为1后再逐层回溯到首次调用位置。但实际运行时,它瞬间完成了所有递归调用,EBX被正确设为1,数组也正确写入了数值,指令指针直接跳到了下一行MOV EAX, EAX处。
请问这是什么原因?是编译后的代码会在执行前预先计算完递归调用的结果吗?
这个现象是Visual Studio调试器的单步模式默认行为导致的,和编译期预计算递归结果没有关系。
核心原因
你调试时使用的是**逐过程(Step Over,默认快捷键F10)**模式,该模式的逻辑就是:遇到CALL这类函数调用指令时,不会进入被调用函数内部逐行执行,而是直接执行完被调用函数的全部逻辑(包含你这里所有层级的递归调用流程),然后直接停在当前CALL指令的下一行,也就是你看到的MOV EAX, EAX位置。
解决方法
如果需要跟踪CALL HPSTART内部的递归执行流程,在调试到CALL这一行时,选择**逐语句(Step Into,默认快捷键F11)**操作,调试器就会跳转到HPSTART的第一条指令,你就可以单步跟踪每一层递归的执行过程了。
补充说明
如果你的项目开启了高等级编译优化(比如/O2),编译器确实会对无副作用的常量递归做编译期计算优化,但你这段代码中包含对Perm数组的写入操作,属于有明确副作用的逻辑,编译器不会提前优化掉执行流程,你观察到的执行结果正确也验证了这一点,无需怀疑是编译优化导致的流程异常。
内容的提问来源于stack exchange,提问作者JustJohn

