今天我在汇编中写了一个递归的斐波那契,但它并没有起作用。我用NASM将它编译成目标文件,而不是用gcc编写它的精灵 当我输入1或2时,该功能完美运行,但是当我输入3,4,5,6或更多时,该功能不起作用。 我认为函数调用自身存在问题。
这段代码:
SECTION .data ;init data
str: db "This equal: %d",10,0
SECTION .text ;asm code
extern printf
global main
main:
push ebp
mov ebp,esp
;--------------------
push 03 ; the index
call _fibonacci
add esp,4
push DWORD eax
push str
call printf
;---------------------
mov esp,ebp
pop ebp
ret
这是功能:
_fibonacci:
push ebp
mov ebp,esp
mov ebx, [ebp+8] ;; param n
cmp ebx,0
jne CHECK2
mov eax,0
jmp _endFIbofunc
CHECK2:
cmp ebx,0x1
jne ELSE3
mov eax,1
jmp _endFIbofunc
ELSE3:
mov ebx,[ebp+8]
dec ebx ;; n-1
;; FIRST call
push ebx
call _fibonacci
add esp,4
mov edx,eax
;; SEC CALL
dec ebx
push ebx
call _fibonacci
add esp,4
add eax,edx
mov eax,[ebp-4]
_endFIbofunc:
mov esp,ebp
pop ebp
ret
我在Ubuntu 16.04上运行后发送错误:
分段错误(核心转储)
可能是什么问题?
答案 0 :(得分:0)
您必须在递归调用中存储(推送)您要更改的寄存器。然后恢复其原始值(弹出)。这应该可以解决问题。
这样的事情:
答案 1 :(得分:0)
mov eax,[ebp-4]
您正在[ebp-4]
使用内存而没有放入有用的东西!
您需要在函数序言中保留此空间:
_fibonacci:
push ebp
mov ebp, esp
sub esp, 4
从第一次递归调用返回时,将EAX
的结果放在此记忆词中
从第二次递归调用返回时,您将此内存dword的内容添加到EAX
这样,EDX
寄存器将不再被破坏。
为什么要使用EBX
注册?如果您使用它,您必须保留它,如Al Kepp在答案中所解释的那样
如果你首先将参数放在EAX
中,你知道对于低于2的两个值(所以0和1),结果就等于参数。简单。
mov eax, [ebp+8] ;; param n
cmp eax, 2
jb _endFIbofunc
如果你在第一次递归调用后没有直接平衡堆栈,你可以只减少已存在的dword并进行第二次递归调用。
dec eax ; n-1
push eax ;(*)
call _fibonacci
mov [ebp-4], eax
dec dword ptr [esp] ; n-2
call _fibonacci
add esp,4 ;(*)
add eax, [ebp-4]
整个过程:
_fibonacci:
push ebp
mov ebp, esp
sub esp, 4 ;(*)
mov eax, [ebp+8] ;; param n
cmp eax, 2
jb _endFIbofunc
dec eax ; n-1
push eax ;(*)
call _fibonacci
mov [ebp-4], eax
dec dword ptr [esp] ;(*) n-2
call _fibonacci
add esp,4 ;(*)
add eax, [ebp-4]
_endFIbofunc:
mov esp, ebp
pop ebp
ret
答案 2 :(得分:0)
除了提供的其他答案之外,这是另一种解决方案:
_fibonacci:
mov eax,[esp+4] ;eax = n
cmp eax,2 ;br if n < 2
jb _endFIbofunc
dec eax ;push n-1
push eax
call _fibonacci ;returns eax = fib(n-1)
xchg eax,[esp] ;eax = n-1, [esp] = fib(n-1)
dec eax ;push n-2
push eax
call _fibonacci ;returns eax = fib(n-2)
add eax,[esp+4] ;eax = fib(n-1)+fib(n-2)
add esp,8
_endFIbofunc:
ret
Trivia - fib(47)是最大的&lt; 2 ^ 32。递归调用的数量= 2 * fib(n + 1)-1。
n fib(n) # calls
0 0 1
1 1 1
2 1 3
3 2 5
4 3 9
5 5 15
6 8 25
7 13 41
8 21 67
9 34 109
10 55 177
11 89 287
12 144 465
13 233 753
14 377 1219
15 610 1973
16 987 3193
17 1597 5167
18 2584 8361
19 4181 13529
20 6765 21891
21 10946 35421
22 17711 57313
23 28657 92735
24 46368 150049
25 75025 242785
26 121393 392835
27 196418 635621
28 317811 1028457
29 514229 1664079
30 832040 2692537
31 1346269 4356617
32 2178309 7049155
33 3524578 11405773
34 5702887 18454929
35 9227465 29860703
36 14930352 48315633
37 24157817 78176337
38 39088169 126491971
39 63245986 204668309
40 102334155 331160281
41 165580141 535828591
42 267914296 866988873
43 433494437 1402817465
44 701408733 2269806339
45 1134903170 3672623805
46 1836311903 5942430145
47 2971215073 9615053951
48 4807526976 ...