首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用间接递归的_cdecl问题

使用间接递归的_cdecl问题
EN

Stack Overflow用户
提问于 2016-04-06 13:48:46
回答 2查看 99关注 0票数 2

我已经写了一个阶乘计算程序,直到最后的计算。当它在_multiply_fact中计算阶乘值时,基指针最终指向一个随机值。我做错了什么?

呼叫

代码语言:javascript
复制
push n
call _factorial
add esp, 4

程序

代码语言:javascript
复制
_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
EN

回答 2

Stack Overflow用户

发布于 2016-04-07 03:49:28

正如您所说,阻止它工作的bug是在递归调用后缺乏修复堆栈。但这并不是中唯一的错误

_multiply_fact并不是一个真正的函数。这只是_factorial代码的一部分,当你没有采用提前输出的n <= 1返回路径时,它就会运行,所以你不应该在其中创建一个堆栈框架。

位于pop ebp _multiply_fact 顶端的_multiply_fact是完全伪造的。它之所以起作用,是因为运行时堆栈的顶部已经有了调用者的ebp。如果您直接将其作为函数调用,则需要用返回地址替换调用者的ebp

首先,您根本不需要创建堆栈帧,因此这完全是对堆栈空间和指令的浪费。(尽管它确实有助于调试器进行回溯,因为如果没有它,它们需要编译器通常添加的信息,但除非您手动添加,否则手写的asm函数就没有这些信息。)

您的_factorial还会破坏调用者的ebx,这违反了ABI。我认为您的函数实际上不会获得正确的值,因为它依赖于在对_factorial的调用中ebx能否存活。在所有的递归调用返回之后,ebx=1,因为每个嵌套的调用都会用它的arg阻塞ebx。

因为你是用asm编写的,所以你可以让你的递归自调用来假设哪些寄存器没有被破坏,或者如果你愿意的话,甚至可以在regs中传递args。不过,您仍然需要以某种方式在递归调用中保存n。一种选择是利用这样一个事实,即你知道_factorial不会在堆栈上重击它的arg (即使ABI允许它这样做)。

不过,您仍然需要可公开访问的包装器函数来遵循ABI。

显然,与循环相比,递归在一开始就是对阶乘的一大浪费时间,但这里有一个尽可能不糟糕的版本。

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2016-04-06 14:12:48

好的,经过更多的故障排除后,我发现这个错误是因为我忘了包含

代码语言:javascript
复制
add esp, 4

在_multiply_fact过程中调用_factorial之后

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36442552

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档