目前,我正在使用NASM学习x86的汇编。我试图编写以下函数来计算斐波那契数:
unsigned int fibonacci(unsigned int n) {
if (n < 2)
return 1;
else
return fibonacci(n - 1) + fibonacci(n - 2);
}
这是我到目前为止所做的,并且效果很好,就像上面的方法一样。但是据我了解,enter 0,0
指令在那里释放了堆栈中用于局部变量的空间。我将两个局部变量推入堆栈:push dword ptr[n]
。但是,仅当我使用enter
这样的enter 8,0
指令时,才应该这样做。为两个int变量释放空间。如果尝试这样做,则会出现StackOverflow异常。
__declspec(naked) unsigned int fibonacci2(unsigned int n) {
__asm {
enter 0,0
cmp dword ptr [n], 2
jae elsee
mov eax, 1
jmp end
elsee:
push dword ptr[n] // Here I am pushing two local variables to the stack.
push dword ptr[n] // 2 * 4 Bytes
dec [ebp-4]
dec [ebp-8]
dec [ebp-8]
push[ebp-4]
call fibonacci2
mov [ebp-4], eax
push [ebp-8]
call fibonacci2
add eax, [ebp-4]
end:
leave
ret
}
}
这是函数的编译版本:
00C426E0 enter 0,0
00C426E4 cmp dword ptr [n],2
00C426E8 jae fibonacci2+11h (0C426F1h)
00C426EA mov eax,1
00C426EF jmp end (0C42716h)
elsee:
00C426F1 push dword ptr [n]
00C426F4 push dword ptr [n]
00C426F7 dec byte ptr [ebp-4]
00C426FA dec byte ptr [ebp-8]
00C426FD dec byte ptr [ebp-8]
00C42700 push dword ptr [ebp-4]
00C42703 call _fibonacci2 (0C413BBh)
00C42708 mov dword ptr [ebp-4],eax
00C4270B push dword ptr [ebp-8]
00C4270E call _fibonacci2 (0C413BBh)
00C42713 add eax,dword ptr [ebp-4]
end:
00C42716 leave
00C42717 ret
答案 0 :(得分:2)
为两个int变量释放空间。
编译器不需要将堆栈用于局部变量。在某些情况下,它甚至可以完全优化堆栈使用,并将局部变量存储在寄存器中。
我将两个局部变量压入堆栈:压入dword ptr [n]。但是,仅当我使用此类输入指令时,才应该这样做:输入8,0。
enter
指令与您认为的指令完全相反:
它不会不分配(堆栈)内存,push
指令可以使用该内存,但是它使用(堆栈)内存的方式与{{ 1}}指令可以:
push
的工作方式类似于先进行enter 80,0
,然后以10个 random 值执行10次enter 0,0
。这对于在堆栈上“创建” 10个未初始化的局部变量很有用。
已经写入push
,将仅enter 0,0
并初始化push
寄存器。使用启用了优化的真实C编译器,在这种情况下,您可能不会获得ebp
指令。
如果我尝试这样做,则会出现StackOverflow异常。
很难说为什么:
如果您大量调用函数,则函数将需要大量堆栈。通过使用enter
而不是enter 8,0
,您需要为函数参数enter 0,0
增加8*n
个字节。
如果使用n
时堆栈已经快满了,那么使用enter 0,0
时堆栈肯定会满了。
第二件事是您的拆卸未完成:
enter 8,0
处添加了一些名为_fibonacci2
的包装器(带有下划线)。0C413BBh
显示为n
,而是显示为ebp+8
。在您发布的代码中可能看不到错误:
n
0C413BBh
未被正确替换(n
)