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来判断跳转
两个循环的区别:让我们先看看为什么需要两个不同的循环?
- for循环:也称为计数循环,通过次数来控制
- while循环:也称条件循环,通过条件来循环
可见两个循环,都是通过jge来比较和跳转,唯一不同的是,for循环使用堆栈,ebp-8来存储变量,并且控制循环。
而while循环使用局部变量或者全局变量来控制循环,这个变量可以是布尔值也可以是数字
而在我们的眼里,无非是内存和立即数的比较,然后通过jge来跳转,本质上没什么区别!
do…while循环
先执行一次条件语句再进行循环
do...while循环与while循环并没有什么本质区别,就是在流程方面不太一样而已!
do...while是先执行功能代码,再判断是否合理,而while是先判断是否合理再执行功能
- 递归
什么是递归?
递归通俗来讲就是不停的调用自身,直到循环条件结束
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