汇编中的递归
Recursion in Assembly
我需要有关刚开始学习的汇编代码的帮助。
.intel_syntax noprefix;
.text;
.globl main;
main:
mov eax, 3;
mov ebx, 0;
push eax;
push ebx;
call f;
add esp, 8;
push eax;
mov eax, offset message;
push eax;
call printf
add esp,8;
mov eax,0;
ret;
f:
mov eax, [esp+8];
mov ebx, [esp+4];
cmp eax,3;
jge ety2;
cmp eax,2;
je ety1;
cmp eax,0;
je ety1;
cmp eax,1;
je ety3;
ety3:
mov eax,0;
ret;
ety1:
mov eax,1;
ret;
ety2:
xor ebx,ebx;
dec eax;
push eax;
push ebx;
call f;
add esp,8;
add ebx,[esp+4];
add ebx,eax;
mov eax,[esp+8];
dec eax;
dec eax;
push eax;
push ebx;
call f;
add esp,8;
add ebx,[esp+4];
add ebx,eax;
add ebx,eax;
mov eax,[esp+8];
dec eax;
dec eax;
dec eax;
push eax;
push ebx;
call f;
add esp,8;
add ebx,[esp+4];
sub ebx,eax;
mov eax,[esp+8];
mov eax,ebx;
ret;
.data;
message:
.asciz "Result=%i\n";
.att_syntax prefix;
在主函数中 'eax' 寄存器用作函数的 'n' 参数:
for n=0 or n=2 returns 1;
for n=1 returns 0;
for n>=3 returns f(n-1)+(2*f(n-2))-f(n-3);
所以对于 n=3 函数 returns 0, n=4 returns 2, n=5 returns 1, n=6 returns 5 e.t.c.
递归有很多问题,对于值 < 5 函数工作正常,但对于 6、7 e.t.c。 function returns 极高或极低(负)值。
我已经为此工作了 +10 个小时,但我无法让它发挥作用
属性。我究竟做错了什么?
需要使用"PUSH"和“[esp+4]”、"add esp,4;"等代码中已有的简单指令。
程序在-m32命令参数下编译(gcc -Wall funcas.s -m32 -o test).
我用 C 写下了相同的代码来展示我想要实现的目标
#include <stdio.h>
#include <stdlib.h>
int funkcja(int n)
{
if(n>=3)
{
return (funkcja(n-1)+(2*funkcja(n-2))-funkcja(n-3));
}
else
{
if(n==2)return 1;
if(n==1)return 0;
if(n==0)return 1;
}
return -1;
}
int main()
{
int a=6;
printf("%d\n", funkcja(a));
return 0;
}
问题在于代码不断累积所有结果。将 f 更改为仅使用一个参数。 Microsoft 类型汇编代码示例。在 f() 和 main() 中,n 都存储在堆栈中。
.model flat,c
; ...
.data
fmt db '%d',00ah,000h
.code
extern printf:proc
public main
f proc ;int f(int n)
mov eax, [esp+4]
cmp eax,3
jge f2
cmp eax,2
je f1
cmp eax,1
je f0
cmp eax,0
je f1
mov eax,-1
ret
f0: mov eax,0
ret
f1: mov eax,1
ret
f2: push ebx ;save ebx
dec eax ;eax = n-1
push eax ;[esp] = n-1
call f ;eax = f(n-1)
mov ebx,eax ;ebx = f(n-1)
dec dword ptr [esp] ;[esp] = n-2
call f ;eax = f(n-2)
add eax,eax ;eax = 2*f(n-2)
add ebx,eax ;ebx = f(n-1) + 2*f(n-2)
dec dword ptr [esp] ;[esp] = n-3
call f ;eax = f(n-3)
add esp,4 ;restore esp
sub ebx,eax ;ebx = f(n-1) + 2*f(n-2) - f(n-3)
mov eax,ebx ;eax = f(n-1) + 2*f(n-2) - f(n-3)
pop ebx ;restore ebx
ret
f endp
main proc near
push dword ptr 0 ;[esp] = n
main0: call f
push eax
push offset fmt
call printf
add esp,8
inc dword ptr [esp]
cmp dword ptr [esp],20
jl main0
add esp,4
xor eax,eax
ret
main endp
我不明白你对 EBX
和堆栈上的第二个参数的操作。
让我们从头开始。递归函数也是一个函数。当你调用它时,你必须保留可以被函数改变的寄存器,并且你需要在函数 return 之后保持不变。该函数以不同的 n
调用自身三次,并以不同的结果运行。当您在堆栈上获得 n
以进行任意恢复时,您必须保留结果。当你将 return (funkcja(n-1)+(2*funkcja(n-2))-funkcja(n-3));
拆分为
时,它会变得更加清晰
int result = 0;
result += funkcja(n-1);
result += ( 2 * funkcja(n-2) );
result -= funkcja(n-3);
return result;
result
是所谓的局部变量。仅此 运行 函数需要它,并且会随函数 return 一起丢失。局部变量通常存储在堆栈中。您不需要使用 prolog 和 epilog 构建堆栈框架,一个简单的 push/pop 组合也可以做到。
# f(n) = f(n-1) + (2*f(n-2)) - f(n-3)
# 0 1
# 1 0
# 2 1
# 3 0 1 + 0 - 1
# 4 2 0 + 2 - 0
# 5 1 2 + 0 - 1
# 6 5 1 + 4 - 0
# 7 5 5 + 2 - 2
# 8 14 5 + 10 - 1
# 9 19 14 + 10 - 5
.intel_syntax noprefix
.text
.globl main
main:
mov eax, 9
push eax
call funkcja
add esp, 4
push eax
mov eax, offset message
push eax
call printf
add esp,8
mov eax,0
ret
funkcja:
mov eax, [esp+4]
cmp eax,3
jge 3f
2:
cmp eax,2
jne 0f
mov eax, 1
ret
0:
cmp eax,0
jne 1f
mov eax, 1
ret
1:
xor eax, eax
ret
3:
push 0 # Result = 0
# 1. Call
mov eax, [esp+8] # +8: retrieve n behind push and return address
sub eax, 1
push eax
call funkcja
add esp, 4
add [esp], eax # Result += EAX
# 2. Call
mov eax, [esp+8] # +8: retrieve n behind push and return address
sub eax, 2
push eax
call funkcja
add esp, 4
add eax, eax
add [esp], eax # Result += EAX
# 3. Call
mov eax, [esp+8] # +8: retrieve n behind push and return address
sub eax, 3
push eax
call funkcja
add esp, 4
sub [esp], eax # Result -= EAX
pop eax # Return EAX = Result
ret
.data;
message: .asciz "Result=%i\n"
.att_syntax prefix
我需要有关刚开始学习的汇编代码的帮助。
.intel_syntax noprefix;
.text;
.globl main;
main:
mov eax, 3;
mov ebx, 0;
push eax;
push ebx;
call f;
add esp, 8;
push eax;
mov eax, offset message;
push eax;
call printf
add esp,8;
mov eax,0;
ret;
f:
mov eax, [esp+8];
mov ebx, [esp+4];
cmp eax,3;
jge ety2;
cmp eax,2;
je ety1;
cmp eax,0;
je ety1;
cmp eax,1;
je ety3;
ety3:
mov eax,0;
ret;
ety1:
mov eax,1;
ret;
ety2:
xor ebx,ebx;
dec eax;
push eax;
push ebx;
call f;
add esp,8;
add ebx,[esp+4];
add ebx,eax;
mov eax,[esp+8];
dec eax;
dec eax;
push eax;
push ebx;
call f;
add esp,8;
add ebx,[esp+4];
add ebx,eax;
add ebx,eax;
mov eax,[esp+8];
dec eax;
dec eax;
dec eax;
push eax;
push ebx;
call f;
add esp,8;
add ebx,[esp+4];
sub ebx,eax;
mov eax,[esp+8];
mov eax,ebx;
ret;
.data;
message:
.asciz "Result=%i\n";
.att_syntax prefix;
在主函数中 'eax' 寄存器用作函数的 'n' 参数:
for n=0 or n=2 returns 1;
for n=1 returns 0;
for n>=3 returns f(n-1)+(2*f(n-2))-f(n-3);
所以对于 n=3 函数 returns 0, n=4 returns 2, n=5 returns 1, n=6 returns 5 e.t.c.
递归有很多问题,对于值 < 5 函数工作正常,但对于 6、7 e.t.c。 function returns 极高或极低(负)值。 我已经为此工作了 +10 个小时,但我无法让它发挥作用 属性。我究竟做错了什么?
需要使用"PUSH"和“[esp+4]”、"add esp,4;"等代码中已有的简单指令。 程序在-m32命令参数下编译(gcc -Wall funcas.s -m32 -o test).
我用 C 写下了相同的代码来展示我想要实现的目标
#include <stdio.h>
#include <stdlib.h>
int funkcja(int n)
{
if(n>=3)
{
return (funkcja(n-1)+(2*funkcja(n-2))-funkcja(n-3));
}
else
{
if(n==2)return 1;
if(n==1)return 0;
if(n==0)return 1;
}
return -1;
}
int main()
{
int a=6;
printf("%d\n", funkcja(a));
return 0;
}
问题在于代码不断累积所有结果。将 f 更改为仅使用一个参数。 Microsoft 类型汇编代码示例。在 f() 和 main() 中,n 都存储在堆栈中。
.model flat,c
; ...
.data
fmt db '%d',00ah,000h
.code
extern printf:proc
public main
f proc ;int f(int n)
mov eax, [esp+4]
cmp eax,3
jge f2
cmp eax,2
je f1
cmp eax,1
je f0
cmp eax,0
je f1
mov eax,-1
ret
f0: mov eax,0
ret
f1: mov eax,1
ret
f2: push ebx ;save ebx
dec eax ;eax = n-1
push eax ;[esp] = n-1
call f ;eax = f(n-1)
mov ebx,eax ;ebx = f(n-1)
dec dword ptr [esp] ;[esp] = n-2
call f ;eax = f(n-2)
add eax,eax ;eax = 2*f(n-2)
add ebx,eax ;ebx = f(n-1) + 2*f(n-2)
dec dword ptr [esp] ;[esp] = n-3
call f ;eax = f(n-3)
add esp,4 ;restore esp
sub ebx,eax ;ebx = f(n-1) + 2*f(n-2) - f(n-3)
mov eax,ebx ;eax = f(n-1) + 2*f(n-2) - f(n-3)
pop ebx ;restore ebx
ret
f endp
main proc near
push dword ptr 0 ;[esp] = n
main0: call f
push eax
push offset fmt
call printf
add esp,8
inc dword ptr [esp]
cmp dword ptr [esp],20
jl main0
add esp,4
xor eax,eax
ret
main endp
我不明白你对 EBX
和堆栈上的第二个参数的操作。
让我们从头开始。递归函数也是一个函数。当你调用它时,你必须保留可以被函数改变的寄存器,并且你需要在函数 return 之后保持不变。该函数以不同的 n
调用自身三次,并以不同的结果运行。当您在堆栈上获得 n
以进行任意恢复时,您必须保留结果。当你将 return (funkcja(n-1)+(2*funkcja(n-2))-funkcja(n-3));
拆分为
int result = 0;
result += funkcja(n-1);
result += ( 2 * funkcja(n-2) );
result -= funkcja(n-3);
return result;
result
是所谓的局部变量。仅此 运行 函数需要它,并且会随函数 return 一起丢失。局部变量通常存储在堆栈中。您不需要使用 prolog 和 epilog 构建堆栈框架,一个简单的 push/pop 组合也可以做到。
# f(n) = f(n-1) + (2*f(n-2)) - f(n-3)
# 0 1
# 1 0
# 2 1
# 3 0 1 + 0 - 1
# 4 2 0 + 2 - 0
# 5 1 2 + 0 - 1
# 6 5 1 + 4 - 0
# 7 5 5 + 2 - 2
# 8 14 5 + 10 - 1
# 9 19 14 + 10 - 5
.intel_syntax noprefix
.text
.globl main
main:
mov eax, 9
push eax
call funkcja
add esp, 4
push eax
mov eax, offset message
push eax
call printf
add esp,8
mov eax,0
ret
funkcja:
mov eax, [esp+4]
cmp eax,3
jge 3f
2:
cmp eax,2
jne 0f
mov eax, 1
ret
0:
cmp eax,0
jne 1f
mov eax, 1
ret
1:
xor eax, eax
ret
3:
push 0 # Result = 0
# 1. Call
mov eax, [esp+8] # +8: retrieve n behind push and return address
sub eax, 1
push eax
call funkcja
add esp, 4
add [esp], eax # Result += EAX
# 2. Call
mov eax, [esp+8] # +8: retrieve n behind push and return address
sub eax, 2
push eax
call funkcja
add esp, 4
add eax, eax
add [esp], eax # Result += EAX
# 3. Call
mov eax, [esp+8] # +8: retrieve n behind push and return address
sub eax, 3
push eax
call funkcja
add esp, 4
sub [esp], eax # Result -= EAX
pop eax # Return EAX = Result
ret
.data;
message: .asciz "Result=%i\n"
.att_syntax prefix