2017-04-06 66 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功能很奇怪。