这个递归函数如何工作

时间:2017-04-06 20:11:28

标签: recursion assembly x86 att

我是编程新手,我开始阅读一本关于它的书来理解基础知识。我无法理解以下汇编代码的工作原理:它计算数字的阶乘。我已经对我能理解的说明添加了评论 - 显然我错过了一些东西。

    .section .data
    .section .text
    .globl _start
    .globl factorial

_start:

    pushl $4             
    call factorial         
    popl %ebx              
    movl %eax, %ebx        
    movl $1, %eax          
    int $0x80

factorial:

    pushl %ebp              # push the base pointer
    movl %esp, %ebp         # copy the stack pointer to the base pointer
    movl 8(%ebp), %eax      # move the value 4 to %eax
    cmpl $1, %eax           # compare it to 1
    je end_factorial        # jump to end_factorial if it's equal
    decl %eax               # else decrease the value by 1
    pushl %eax              # push the decreased value in the stack
    call factorial          # this means that it should start again (?)

    popl %ebx               # that's the part that i don't understand
    incl %ebx               # when are these lines excuted? since it
    imul %ebx, %eax         # keeps starting from the top, until the value
                            # of %eax is 1 then, it jumps to end_factorial

end_factorial:

    movl %ebp, %esp
    popl %ebp
    ret`

1 个答案:

答案 0 :(得分:0)

不要以文字方式评论,而是将评论放在上下文中。

不要写将值4移动到%eax ,而是找到含义: n 移动到eax
不要跟踪寄存器值,跟踪变量:否则将值减1 更好 eax = n - 1

如果您再次尝试评论该程序,则应该达到以下内容。

.section .data
.section .text

.globl _start                 
.globl factorial

_start:

    pushl $4             
    call factorial          # eax = 4! 
    popl %ebx               # Remove the parameter from the stack       

    movl %eax, %ebx
    movl $1, %eax          
    int $0x80               # sys_exit(4!)

factorial:

    pushl %ebp
    movl %esp, %ebp         # Prolog

    movl 8(%ebp), %eax      # eax = n

    cmpl $1, %eax           # n == 1?
    je end_factorial        # If yes, since 1! = 1, just return

    decl %eax               # eax = n - 1

    pushl %eax              
    call factorial          # eax = (n - 1)!

    popl %ebx               # ebx = (n - 1)
    incl %ebx               # ebx = n
    imul %ebx, %eax         # eax = n * (n - 1)! = n!

end_factorial:

    movl %ebp, %esp         # Prolog
    popl %ebp
    ret

通过这些评论,功能正在运行 - 它是一个非常标准的,非尾递归,因子实现。

int fact(int n)
{
   if (n == 1)
      return 1;

   return n * fact(n-1);
}

有关执行流程的问题,特别是在递归关闭后执行的代码,可以在使用铅笔和橡胶后回答。
最后,您将看到要查找的重要部分是终止条件终止案例) - 它是不会跨越任何递归调用的输入。
在此示例中 n = 1

充分理解函数所需的另一个支柱是函数如何实际工作 - 每个调用都是一个唯一的实例,并且函数返回执行后,调用者继续使用调用者的状态 (恢复调用者调用) 从而创建已保存/恢复状态的(抽象)堆栈

该实现的唯一特殊方面是用于清理函数参数堆栈的指令 如果上面的句子让你失望,我建议你阅读calling conventions 通常使用addl $4, %esp,代码中使用popl %ebx代替 - 虽然它在factorial正文中有意义,因为在递归调用之后需要再次n,使用在_start函数中很奇怪。