2017-04-06 71 views
1

我新的编程,和我开始阅读一本书就明白了基本面。我无法理解下面的汇编代码是如何工作的:它计算一个数字的阶乘。我已经对我能理解的说明添加了评论 - 显然我错过了一些东西。怎么做这个递归函数的工作

.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` 
+0

哪一部分难以理解?哪部分不起作用? – Hodrobond

+0

该程序的工作原理,我不明白的唯一部分是递归函数阶乘,以及它如何计算,并感谢回应 – ech0

回答

0

不要评论acontextually,而是将评论放在上下文中。

不要写移动值4%EAX,而不是寻找意义:移动ň到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功能很奇怪。