如何使用传递到当前子例程中的参数调用不同的子例程(用于递归)
How do I call a different subroutine using parameters passed into the current one (for use in recursion)?
我有两个函数,从输入中读取整数x和y。
产品退货 x * y
幂返回 x ^ y,但它使用递归和乘积来计算这一点。 所以 x 是"基数",y 是"指数"。
他们从C++打电话:
int a, b, x, y;
a = product(x, y);
b = power(x, y);
这是 ASM。我让产品工作,但是在电源方面遇到了问题,因为我不确定从中调用产品的语法/方法/约定(并为递归调用自身(。编辑:必须使用递归。
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
编辑:我的解决方案!
power:
; Standard prologue
push ebp ; Save the old base pointer
mov ebp, esp ; Set new value of the base pointer
sub esp, 4 ; make room for 1 local variable result
push ebx ; this is exp-1
xor eax, eax ; Place zero in EAX. We will keep a running sum
mov eax, [ebp+12] ; exp
mov ebx, [ebp+8] ; base
cmp eax, 1 ; n >= 1
jge L1 ; if not, go do a recursive call
mov eax, 1 ; otherwise return 1
jmp L2
L1:
dec eax ; exp-1
push eax ; push argument 2: exp-1
push ebx ; push argument 1: base
call power ; do the call, result goes in eax: power(base, exp-1)
add esp, 8 ; get rid of arguments
push eax ; push argument 2: power(base, exponent-1)
push ebx ; push argument 1: base
call product ; product(base, power(base, exponent-1))
L2:
; Standard epilogue
pop ebx ; restore register
mov esp, ebp ; deallocate local variables
pop ebp ; Restore the callers base pointer.
ret ; Return to the caller.
您使用的是 CDECL 调用约定,因此您必须首先向后推送堆栈中的参数,然后调用函数,然后在返回后清理堆栈。
push arg_last
push arg_first
call MyFunction
add esp, 8 ; the argument_count*argument_size
但这里有一些关于你的代码的注释:
您的函数
product
不返回任何值。在标签后立即使用mov eax, [ebp-4]
product_done
。乘法很容易通过指令
mul
或imul
进行。使用加法是最慢的方法。通过递归计算功率并不是最好的主意。使用以下算法:
Y = 1;
如果 N=0 退出。
如果 N 为奇数 -> Y = Y*x;N=N-1
如果 N 为偶数 -> Y = Y*Y;N=N/2
转到 2
使用SHR
指令将 N 除以 2。使用test
输入以检查奇数/偶数。
这样,您就不需要从函数调用product
power
。
如果你不确定如何编写程序集,你通常可以用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 的编译器 ala cl /Fa power.cc
编译:
; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01
TITLE C:homeanthonyuserdevpower.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:homeanthonyuserdevpower.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
以上是 power 函数的入口代码 - 只需调整堆栈指针以跳过函数参数,它将在下面以_exp$[ebp]
(即exp
(和_n$[ebp]
(即 n
(。
; Line 7
cmp DWORD PTR _exp$[ebp], 0
jne SHORT $LN5@power
mov DWORD PTR tv74[ebp], 1
jmp SHORT $LN6@power
基本上,如果 exp
不等于 0,我们将继续下面的标签 $LN5@power
,但如果它是 0,则在 tv74[ebp]
处将1
加载到堆栈上的返回值位置,并在 $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
同时将 n 推到堆栈上...
call ?power@@YAHHH@Z ; power
递归调用 power 函数,该函数将使用上面的两个值推送。
add esp, 8
返回上述函数后的堆栈调整。
push eax
将递归调用的结果(电源返回指令留在 eax 寄存器中(放到堆栈上...
mov eax, DWORD PTR _n$[ebp]
push eax
同时将 n 推到堆栈上...
call ?product@@YAHHH@Z ; product
调用 product 函数将调用上述power
返回的值乘以 n
。
add esp, 8
mov DWORD PTR tv73[ebp], eax
将product
的结果复制到堆栈上的临时地址中。
$LN4@power:
mov ecx, DWORD PTR tv73[ebp]
mov DWORD PTR tv74[ebp], ecx
从该 tv73 临时位置获取值并将其复制到 tv74...
$LN6@power:
mov eax, DWORD PTR tv74[ebp]
最后,将 tv74 的product()
结果移动到 eax 寄存器中,以便在product
调用返回后方便快捷地访问。
; Line 8
mov esp, ebp
pop ebp
ret 0
清理堆栈并返回。
- 子例程,不使用 pow,并带有参数和返回
- 如何将C++子例程链接到 x86 程序集程序?
- 调用子例程时类型不匹配
- 将分配给C++数组传递给 Fortran 子例程
- Android Studio 3.1.2 - 无法运行C++子例程"No implementation found for Java.lang.String..."
- 试图在C 中调用Fortran子例程
- Visual MSVC C 调用GFORTRAN子例程
- 从C 调用Fortran子例程会产生非法参数值
- 将类对象指针传递给默认构造函数(C 子例程)
- 如何从子例程函数获取到 main 函数的返回值
- 确定未在 Fortran 子例程顶部声明的变量类型
- 子例程在 MIPS 上调用了两次
- 从 C++ 调用 Fortran 子例程
- 将C++函数指针传递到Fortran子例程
- 如何使用传递到当前子例程中的参数调用不同的子例程(用于递归)
- 在主程序或函数子例程中使用数组的差异
- 如何将 fortran 子例程链接到 cpp 主程序
- 将C++数组传递给Fortran子例程会导致nan值出现在结果中
- 单元测试不会失败,但挂在子例程调用上
- 调用子类的子例程后指针消失