- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
您好,我正在使用 NASM 在 Assembly 中编写阶乘函数。我必须使用俄语乘法代替 mul 来完成我的作业。我正在使用 32 位 Linux
这是我的阶乘代码
section .text
global factorial
extern rpmult
factorial:
push ebp
mov ebp, esp
sub esp, 4 ;creates memory for local variable at ebp-4
mov esi, [ebp+8] ; put n in esi
cmp esi, 1 ; n <= 1
jbe .done ; if so jump to done
.try:
mov [ebp-4],esi ;adds n temporarily into ebp-4
dec esi ; n - 1
push esi ; push arugment
call factorial ;call factorial again stores result in esi
add esp, 4 ;gets rid of the argument
mov edi, esi ;copies n - 1 into edi
mov esi,[ebp+4] ;gets the original value back (n)
call rpmult ;multiply
jmp .done ;once it reaches here, finished the function
.done:
mov esp, ebp ;restores esp
pop ebp
ret ;return the value
这是我的 rpmult 代码:
section .text
global rpmult
rpmult:
push ebp
mov ebp, esp
sub esp, 4 ;allocate m
mov dword [ebp-4], 0 ; m = 0;
.while:
test edi, edi ; x == 0?
je .done
test esi, esi ; y == 0?
je .done
test edi, 0x01 ; x is odd?
jz .shifts
add [ebp-4], esi ; m += y;
.shifts:
shr edi, 1 ; x >>= 1;
shl esi, 1 ; y <<= 1;
jmp .while
.done:
mov eax, [ebp-4]
;mov esp, ebp
;pop ebp
leave
ret
当我通过 C 程序使用函数时,说出 4 的阶乘!我明白了
4! = 13803416593125867520
我相信我的代码是正确的,但我不知道该怎么做。我需要让阶乘函数与 rpmult 函数一起用于我的期末考试。任何帮助表示赞赏!谢谢!
最佳答案
(注意:我重写了这个答案,在我更加清醒的时候再次查看它,并阅读了@lloydm 的评论。)
存在三个问题区域:
调试递归函数时,首先检查基本情况总是明智的。
那么在计算 1!
时会发生什么?
factorial:
push ebp
mov ebp, esp
sub esp, 4 ;creates memory for local variable at ebp-4
mov esi, [ebp+8] ; put n in esi
cmp esi, 1 ; n <= 1
jbe .done ; if so jump to done
...
.done:
mov esp, ebp ;restores esp
pop ebp
ret ;return the value
这里已经有两个问题:
您希望这段代码在从 C 调用时能够正常工作,这意味着您需要遵循通常的调用约定(对于带有 gcc 的 Linux 来说,这意味着“cdecl”- 参见 http://en.wikipedia.org/wiki/X86_calling_conventions)。所以你需要保留esi
、edi
、ebp
和ebx
。但是这段代码覆盖了 esi
中的任何内容。当从 C 调用该函数时,这将导致不可预测的行为,因为 C 编译器生成的代码将假定调用 factorial
之前 esi
中的任何内容仍然存在当它返回时。只有先将它们的值保存在某个地方(并在返回之前恢复它们),才能使用这些寄存器。
返回值在 eax
中传递,但您并未在此处向 eax
中放入任何内容。您希望 1!
的答案是 1
,而不是“此时 eax
中碰巧出现的任何随机垃圾”!
...
.try:
mov [ebp-4],esi ;adds n temporarily into ebp-4
dec esi ; n - 1
push esi ; push arugment
call factorial ;call factorial again stores result in esi
add esp, 4 ;gets rid of the argument
mov edi, esi ;copies n - 1 into edi
mov esi,[ebp+4] ;gets the original value back (n)
call rpmult ;multiply
jmp .done ;once it reaches here, finished the function
...
edi
,和esi
一样,是需要保存的寄存器,如上所述。
行 mov edi, esi ;copys n - 1 into edi
是错误的。您不想将 n - 1
放入 edi
- 您正在尝试计算 (n-1)!*n
,所以你想将 (n-1)!
放入 edi
,即递归调用计算的答案。正如@lloydm 指出的那样,它在 eax
中返回。 (我被我原来回答中的评论误导了,以为你真的想把 n - 1
放到 edi
中。那也行不通,因为 esi
在 call factorial
之后不再包含 n - 1
,因为您不遵循调用约定。)
mov esi,[ebp+4] ;gets the original value back (n)
是错误的(正如我最初指出的); [ebp+4]
包含返回地址;这应该是 [ebp-4]
。
4! = 13803416593125867520
是一个比它第一次出现时更奇怪的答案:它对于 32 位值来说太大了。 (十六进制:0xbf8f964200000000
,因此它是一个 64 位值,前 32 位为大数,后 32 位为零。)
考虑到其他错误,您可能希望得到一个完全随机的值作为答案,但 factorial
返回一个 32 位随机值。那么为什么要在这里打印 64 位值呢? (如果你不是故意这样做,我想这可能与 C 代码做了一些奇怪的事情有关,因为 esi
和 edi
没有被你的代码保留。 )
不要一开始就试图找出为什么 factorial(5)
不起作用。使用 factorial(1)
尽可能简单地开始。然后处理 factorial(2)
等
关于linux - 组装 - NASM 析因问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8453666/
我是一名优秀的程序员,十分优秀!