C语言再学习4-循环-为什么尽量避免递归

1. for循环

for (int i=0;i<5;i++)
{

}
//如果变量i小于5,就一直执行,每次执行一次然后变量i自己加1

一个for循环就这么简单, 可是为什么i能控制这个循环的次数?我们应该反过来学习:

	for (int i = 0; i < 5; i++) {
00D417AF C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0  
00D417B6 EB 09                jmp         main+31h (0D417C1h)  
00D417B8 8B 45 F8             mov         eax,dword ptr [ebp-8]  
00D417BB 83 C0 01             add         eax,1  
00D417BE 89 45 F8             mov         dword ptr [ebp-8],eax  
00D417C1 83 7D F8 05          cmp         dword ptr [ebp-8],5  
00D417C5 7D 02                jge         main+39h (0D417C9h)  
//
00D417C7 EB EF                jmp         main+28h (0D417B8h)  
	}

这是一个空的for循环,让我们来看看

mov         dword ptr [ebp-8],0          //这行应该就是我们的int i=0了
00D417B6 EB 09                jmp         main+31h (0D417C1h)  
紧接着就直接跳转到了
cmp         dword ptr [ebp-8],5                       //0D417C1h
然后一个jcc,jge,让我们来复习一下jge是什么?
jge 大于等于则跳转(有符号数) sf=of

结论:循环控制条件变量i存在ebp-8的位置,把0放到ebp-8以后(int i=0),jmp到(i是否大于等于5),如果i大于等于5,就跳出循环,if(i>){break;},由于在汇编中用大于等于就跳出来表示小于5则继续执行,所以ebp-8(变量i)并没有小于5,则继续执行,然后jmp回i++,变量i加完以后继续执行比较,由次重复

跳转到这行语句以后,拿着5和ebp-8来比较,这里证明我们上面的猜想是正确的,ebp-8存了我们的变量i

在这里插入图片描述
可见ebp-4这个地址并没有使用,那我们能不能把ebp-4当成一个参数的入口来使用呢?
ebp-8等于1,是因为我的if语句控制在,当变量i等于1的时候才执行这些汇编语句(为了值执行一次)

简单描述一下这个代码的作用:
当我们通过实验得出结论,for循环的第一个参数是自身的控制循环用的变量,而这个变量存在ebp-8



while循环和do…while循环

int i;
while (i<5)
{
	i++;
}
//我常使用的方式

让我们先来看看反汇编,for循环如此强大, 为什么还要while循环?

	int i=0;
000517B8 C7 45 F8 00 00 00 00 mov         dword ptr [i],0  
	while (i<5)
000517BF 83 7D F8 05          cmp         dword ptr [i],5  
000517C3 7D 0B                jge         main+40h (0517D0h)  
	{

		i++;
000517C5 8B 45 F8             mov         eax,dword ptr [i]  
000517C8 83 C0 01             add         eax,1  
000517CB 89 45 F8             mov         dword ptr [i],eax  
	}

。。。
定义一个变量i,把变量i和控制循环次数5比较,通过jcc中的jge来判断跳转
两个循环的区别:让我们先看看为什么需要两个不同的循环?

  1. for循环:也称为计数循环,通过次数来控制
  2. while循环:也称条件循环,通过条件来循环

可见两个循环,都是通过jge来比较和跳转,唯一不同的是,for循环使用堆栈,ebp-8来存储变量,并且控制循环。
而while循环使用局部变量或者全局变量来控制循环,这个变量可以是布尔值也可以是数字

而在我们的眼里,无非是内存和立即数的比较,然后通过jge来跳转,本质上没什么区别!


do…while循环
先执行一次条件语句再进行循环

do...while循环与while循环并没有什么本质区别,就是在流程方面不太一样而已!
do...while是先执行功能代码,再判断是否合理,而while是先判断是否合理再执行功能


  1. 递归

什么是递归?

递归通俗来讲就是不停的调用自身,直到循环条件结束

int Func(int n)
{
if(n < 2)
return 1;
else
return n*Func(n-1);
}

int main()
{
int n = 5;
printf("n! = %d\n",Func(n));

return 0;
}

让我们来看看反汇编

	00EE420F C7 45 F8 05 00 00 00 mov         dword ptr [n],5  
	int n = 5;
	printf("n! = %d\n", Func(n));
00EE4216 8B 45 F8             mov         eax,dword ptr [n]  
00EE4219 50                   push        eax  
00EE421A E8 74 D1 FF FF       call        Func (0EE1393h)  
00EE421F 83 C4 04             add         esp,4  
00EE4222 50                   push        eax  
00EE4223 68 CC 7B EE 00       push        offset string "n! = %d\n" (0EE7BCCh)  
00EE4228 E8 4D D1 FF FF       call        _printf (0EE137Ah)  
00EE422D 83 C4 08             add         esp,8  
int Func(int n)
{
00EE2560 55                   push        ebp  
00EE2561 8B EC                mov         ebp,esp  
00EE2563 81 EC C0 00 00 00    sub         esp,0C0h  
00EE2569 53                   push        ebx  
00EE256A 56                   push        esi  
00EE256B 57                   push        edi  
00EE256C 8D BD 40 FF FF FF    lea         edi,[ebp-0C0h]  
00EE2572 B9 30 00 00 00       mov         ecx,30h  
00EE2577 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
00EE257C F3 AB                rep stos    dword ptr es:[edi]  
00EE257E B9 08 C0 EE 00       mov         ecx,offset _0340B6FD_consoleapplication3.cpp (0EEC008h)  
00EE2583 E8 80 EC FF FF       call        @__CheckForDebuggerJustMyCode@4 (0EE1208h)  
	if (n < 2)
00EE2588 83 7D 08 02          cmp         dword ptr [n],2  
00EE258C 7D 09                jge         Func+37h (0EE2597h)  
		return 1;
00EE258E B8 01 00 00 00       mov         eax,1  
00EE2593 EB 15                jmp         Func+4Ah (0EE25AAh)  
	else
00EE2595 EB 13                jmp         Func+4Ah (0EE25AAh)  
		return n * Func(n - 1);
00EE2597 8B 45 08             mov         eax,dword ptr [n]  
00EE259A 83 E8 01             sub         eax,1  
00EE259D 50                   push        eax  
00EE259E E8 F0 ED FF FF       call        Func (0EE1393h)  
00EE25A3 83 C4 04             add         esp,4  
00EE25A6 0F AF 45 08          imul        eax,dword ptr [n]  
}
00EE25AA 5F                   pop         edi  
00EE25AB 5E                   pop         esi  
00EE25AC 5B                   pop         ebx  
00EE25AD 81 C4 C0 00 00 00    add         esp,0C0h  
00EE25B3 3B EC                cmp         ebp,esp  
00EE25B5 E8 58 EC FF FF       call        __RTC_CheckEsp (0EE1212h)  
00EE25BA 8B E5                mov         esp,ebp  
00EE25BC 5D                   pop         ebp  
00EE25BD C3                   ret  

这是我们的递归函数
我们发现在

00EE259E E8 F0 ED FF FF       call        Func (0EE1393h)  

这个位置,又调用了自身函数,一直调用。最后总结:
执行过程:
-》Func(5)
-》5Func(4)
-》5
(4Func(3))
-》5
(4*(3Func(2))))
-》5
(4*(3*(2*Func(1))))

当n为0的时候停止递归,返回结果
由于遇到1的时候返回1,那么Func(1)=1

所以结果是5*(4*(3*(2*1))) = 120

递归就是不停的调用自身,所以非常消耗资源,甚至造成堆栈溢出和程序崩溃等等问题!

猜你喜欢

转载自blog.csdn.net/qq_35425243/article/details/82835320