C++ 如何使用传入当前子例程的参数调用不同的子例程(用于递归)?
我有两个函数,从输入读取整数x和y 产品返回x*y power返回x^y,但它使用递归和乘积来计算。所以x是“基”,y是“指数” 他们从C++调用:C++ 如何使用传入当前子例程的参数调用不同的子例程(用于递归)?,c++,assembly,recursion,x86,C++,Assembly,Recursion,X86,我有两个函数,从输入读取整数x和y 产品返回x*y power返回x^y,但它使用递归和乘积来计算。所以x是“基”,y是“指数” 他们从C++调用: int a, b, x, y; a = product(x, y); b = power(x, y); 这是asm。我让产品正常工作了,但是我在power方面遇到了麻烦,因为我不确定从它调用产品(并为递归调用自身)的语法/方法/约定。编辑:必须使用递归 global product global power secti
int a, b, x, y;
a = product(x, y);
b = power(x, y);
这是asm。我让产品正常工作了,但是我在power方面遇到了麻烦,因为我不确定从它调用产品(并为递归调用自身)的语法/方法/约定。编辑:必须使用递归
global product
global power
section .text
product:
push ebp
mov ebp, esp
sub esp, 4
push edi
push esi
xor eax, eax
mov edi, [ebp+8]
mov esi, [ebp+12]
mov [ebp-4], edi
product_loop:
add [ebp-4], edi
mov eax, [ebp-4]
sub esi, 1
cmp esi, 1
jne product_loop
product_done:
pop esi
pop edi
mov esp, ebp
pop ebp
ret
power:
push ebp
mov ebp, esp
sub esp, 4
push edi
push esi
push ebx
xor eax, eax
mov edi, [ebp+8]
mov esi, [ebp+12]
;;;
check:
cmp esi, 1 ; if exp < 1
jl power_stop
recursion: ; else (PLEASE HELP!!!!!!!!)
; eax = call product (base, (power(base, exp-1))
power_stop:
mov eax, 1 ; return 1
power_done:
push ebx
pop esi
pop edi
mov esp, ebp
pop ebp
ret
您使用的是调用约定,因此必须首先向后推堆栈中的参数,然后调用函数,然后在返回后清理堆栈
push arg_last
push arg_first
call MyFunction
add esp, 8 ; the argument_count*argument_size
但这里有一些关于代码的注释:
产品
不返回任何值。在产品完成后立即使用mov eax,[ebp-4]
mul
或imul
进行乘法非常容易。使用加法是最慢的方法SHR
指令将N除以2。使用test
指令检查奇数/偶数
这样,您就不需要从<代码> POWER < /COD>函数> <代码/> > > < P> > P >如果您不确定如何编写程序集,您可以一般用C++编写并将其组装成线索-类似于:
int power(int n, int exp)
{
return exp == 0 ? 1 :
exp == 1 ? n :
product(n, power(n, exp - 1));
}
然后,您应该能够使用gcc-S
或编译器用于汇编输出的等效开关,或者如果愿意,可以反汇编机器代码
例如,上面的函数与int-product(int-x,int-y){return x*y;}
和int-main(){return-product(3,4);}
一起抛出,使用Microsoft编译器alacl/Fa-power.cc
编译:
; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01
TITLE C:\home\anthony\user\dev\power.cc
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC ?product@@YAHHH@Z ; product
; Function compile flags: /Odtp
_TEXT SEGMENT
_x$ = 8 ; size = 4
_y$ = 12 ; size = 4
?product@@YAHHH@Z PROC ; product
; File c:\home\anthony\user\dev\power.cc
; Line 1
push ebp
mov ebp, esp
mov eax, DWORD PTR _x$[ebp]
imul eax, DWORD PTR _y$[ebp]
pop ebp
ret 0
?product@@YAHHH@Z ENDP ; product
_TEXT ENDS
PUBLIC ?power@@YAHHH@Z ; power
; Function compile flags: /Odtp
_TEXT SEGMENT
tv73 = -8 ; size = 4
tv74 = -4 ; size = 4
_n$ = 8 ; size = 4
_exp$ = 12 ; size = 4
?power@@YAHHH@Z PROC ; power
; Line 4
push ebp
mov ebp, esp
sub esp, 8
; Line 7
cmp DWORD PTR _exp$[ebp], 0
jne SHORT $LN5@power
mov DWORD PTR tv74[ebp], 1
jmp SHORT $LN6@power
$LN5@power:
cmp DWORD PTR _exp$[ebp], 1
jne SHORT $LN3@power
mov eax, DWORD PTR _n$[ebp]
mov DWORD PTR tv73[ebp], eax
jmp SHORT $LN4@power
$LN3@power:
mov ecx, DWORD PTR _exp$[ebp]
sub ecx, 1
push ecx
mov edx, DWORD PTR _n$[ebp]
push edx
call ?power@@YAHHH@Z ; power
add esp, 8
push eax
mov eax, DWORD PTR _n$[ebp]
push eax
call ?product@@YAHHH@Z ; product
add esp, 8
mov DWORD PTR tv73[ebp], eax
$LN4@power:
mov ecx, DWORD PTR tv73[ebp]
mov DWORD PTR tv74[ebp], ecx
$LN6@power:
mov eax, DWORD PTR tv74[ebp]
; Line 8
mov esp, ebp
pop ebp
ret 0
?power@@YAHHH@Z ENDP ; power
_TEXT ENDS
PUBLIC _main
; Function compile flags: /Odtp
_TEXT SEGMENT
_main PROC
; Line 11
push ebp
mov ebp, esp
; Line 12
push 4
push 3
call ?power@@YAHHH@Z ; power
add esp, 8
; Line 13
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
要引导您完成此操作,请执行以下操作:
?power@@YAHHH@Z PROC ; power
; Line 4
push ebp
mov ebp, esp
sub esp, 8
上面是幂函数的输入代码-只需调整堆栈指针以跳过函数参数,它将在下面作为\u exp$[ebp]
(即exp
)和\u n$[ebp]
(即n
)访问函数参数
基本上,如果exp
不等于0,我们将继续标签$LN5@power
如下,但如果为0,则将1
加载到堆栈上tv74[ebp]
处的返回值位置,并跳转到$LN6@power
$LN5@power:
cmp DWORD PTR _exp$[ebp], 1
jne SHORT $LN3@power
mov eax, DWORD PTR _n$[ebp]
mov DWORD PTR tv73[ebp], eax
jmp SHORT $LN4@power
与上面类似-如果exp为1,则将n放入eax,并从中放入返回值堆栈内存,然后跳转到返回指令
现在它开始变得有趣
$LN3@power:
mov ecx, DWORD PTR _exp$[ebp]
sub ecx, 1
push ecx
从exp中减去1并将其推入堆栈
mov edx, DWORD PTR _n$[ebp]
push edx
call ?power@@YAHHH@Z ; power
mov eax, DWORD PTR _n$[ebp]
push eax
call ?product@@YAHHH@Z ; product
同时将n推到堆栈上
mov edx, DWORD PTR _n$[ebp]
push edx
call ?power@@YAHHH@Z ; power
mov eax, DWORD PTR _n$[ebp]
push eax
call ?product@@YAHHH@Z ; product
递归调用幂函数,该函数将使用上面的两个值
add esp, 8
在上述函数返回后进行堆栈调整
push eax
将递归调用的结果(电源返回指令留在eax寄存器中)放入堆栈
mov edx, DWORD PTR _n$[ebp]
push edx
call ?power@@YAHHH@Z ; power
mov eax, DWORD PTR _n$[ebp]
push eax
call ?product@@YAHHH@Z ; product
同时将n推到堆栈上
mov edx, DWORD PTR _n$[ebp]
push edx
call ?power@@YAHHH@Z ; power
mov eax, DWORD PTR _n$[ebp]
push eax
call ?product@@YAHHH@Z ; product
调用product函数,将调用上面的power
返回的值乘以n
; Line 7
cmp DWORD PTR _exp$[ebp], 0
jne SHORT $LN5@power
mov DWORD PTR tv74[ebp], 1
jmp SHORT $LN6@power
add esp, 8
mov DWORD PTR tv73[ebp], eax
将产品的结果
复制到堆栈上的临时地址
$LN4@power:
mov ecx, DWORD PTR tv73[ebp]
mov DWORD PTR tv74[ebp], ecx
从tv73临时位置获取值并将其复制到tv74
$LN6@power:
mov eax, DWORD PTR tv74[ebp]
最后,将product()
结果从tv74移动到eax寄存器中,以便在product
调用返回后方便快捷地访问
; Line 8
mov esp, ebp
pop ebp
ret 0
清理堆栈并返回。1st:这里没有使用递归。这只是一个练习吗?编译器生成的代码将比这更快…是的,这只是一个练习@阿图尔:也许是这样,但我正在努力!我不知道怎么做。只需将
参数推送到堆栈上,然后调用产品子例程即可。返回值也将在堆栈上(pop
it aftercall
)。也就是说…为什么要递归?您使用的是循环,它不是递归(也不需要)。要进行递归,您必须从内部调用power
,您的递归
标签毫无意义。调用product
然后为power
推送
新操作数,并再次调用它,直到满足停止条件(递归…@Tidus)-首先练习调用Convention use-编写简单函数,取2个整数并返回1个整数。根据调用约定,您还必须向调用方添加一些代码-请参阅反汇编和读取中的例程调用。如果你明天做不好,我就帮你;-)啊,我错过了循环中的moveax[ebp-4]
。但是你从来没有实际使用过它,所以最好从循环中删除它。实际上最好完全停止使用[ebp-4],而使用eax寄存器作为变量。最好不要使用循环。:)使用mul
或imul
.1。这不是在产品循环标签中完成的吗?在product_done中,我做了一些事情,比如恢复寄存器、释放局部变量、恢复基指针等,以实现约定。2-3. 我知道这些都是做这些事情的可怕方式,但这是一种试图让我的头脑完全清醒过来的练习。你能告诉我如何使用递归吗?基本上我是在努力实现这一点:在我的能力范围内function@TidusSmith-不,对不起。必须从一开始就阻止不良做法。:)检查我的解决方案。这样好一点吗?老实说,我很高兴它能起作用,但我可以,我应该尽快学会如何正确地做。我真正需要知道并且似乎已经发现的是,传入的参数只是随便什么