我已经写了一个阶乘计算程序,直到最后的计算为止。当它计算_multiply_fact
中的阶乘值时,基指针最终指向随机值。我做错了什么?
调用
push n
call _factorial
add esp, 4
程序
_factorial:
push ebp ;save base pointer
mov ebp, esp ;set up new base pointer
mov ebx, 8[ebp]
cmp ebx, 1 ;while n > 0
jg _multiply_fact ;decrement n
mov eax, 1 ;if n == 0 ret 1
pop ebp
ret ;return to multipy_fact for calculation
_multiply_fact:
pop ebp
push ebp
mov ebp, esp
mov ebx, 8[ebp]
dec ebx ;decrements n-1 times
push ebx
call _factorial
inc ebx
mul ebx ;recursively multiplies eax * (n+1)
pop ebp
ret
答案 0 :(得分:1)
正如你所说,阻止它工作的错误是在递归调用之后没有修复堆栈。但并非唯一的错误
_multiply_fact
实际上不是一个功能。它只是_factorial
代码的一部分,当您没有采用早期n <= 1
返回路径时,它会运行。所以你应该不在其中制作堆栈帧。
pop ebp
顶部的_multiply_fact
完全是假的。它只有可以工作,因为运行时堆栈的顶部已经有调用者ebp
。如果您直接将其作为一项功能进行调用,则可以使用返回地址对来电者ebp
进行删除。
首先,您根本不需要制作堆栈框架,因此完全浪费了堆栈空间和指令。 (虽然它确实有助于调试器做回溯,因为没有它,他们需要编译器通常添加的信息,但手写的asm函数除非你手动添加,否则不会有。)
您的_factorial
也打电话给来电者ebx
,这违反了ABI。我认为你的功能实际上不会得到正确的值,因为它取决于ebx
在_factorial
的呼叫中存活。在所有递归调用返回ebx=1
之后,因为每个嵌套的调用都使用它的arg执行ebx。
因为你在asm中写作,你可以让你的递归自我调用假设哪些寄存器没有被破坏,或者如果你想要的话甚至可以在regs中传递args。不过,您仍然需要以某种方式在递归调用中保存n
。一种选择是利用这样一个事实:你知道_factorial
并没有在堆栈上破坏它的arg(即使ABI允许它)。
但是仍然需要可公开访问的包装函数来遵循ABI。
显然,与循环相比,递归对于阶乘而言是一个很大的浪费时间,但这里的版本尽可能少。
global _factorial
_factorial:
; one arg: int n
push ebp
mov ebp, esp ; make a stack frame
mov edx, [ebp + 8] ; load first arg into a scratch reg
dec edx
jg .multiply_fact ; if ((n-1) > 0). Note that dec doesn't set CF, so just use jnz instead of ja if you want to convert this to unsigned
;; base case
mov eax, 1 ;if (n <= 1) ret 1
pop ebp
ret
.multiply_fact: ; not a separate function, still part of _factorial
; in NASM, .labels are local labels that don't show up as symbols in the object file. MASM uses something different.
; at this point: edx = n-1
push edx
call _factorial ; eax = fac(n-1)
pop edx ; get n-1 back off the stack. Taking advantage of extra knowledge of our own internals, since the ABI allows functions to clobber their args on the stack
; add esp, 4 not needed, because we popped instead
inc edx ; undo the earlier dec before we pushed
imul eax, edx ; n * fac(n-1). 2-arg imul runs faster
pop ebp
ret
答案 1 :(得分:0)
好了,经过多一点故障排除后我发现错误是因为我忘了包含
add esp, 4
在_multiply_fact过程中调用_factorial后