首先来看一段goto语句(什么?不知道goto语句,C语言老师太水了吧,这都不教。其实就和无条件跳转指令JMP差不多,但是最好不要用,因为会破坏结构化)的例子:
int GoToDo(intnCount)
{
int nSum =0;
int nIndex =0;
GOTO_DO:
nSum += nIndex;
nIndex++;
if (nIndex<= nCount)
{
gotoGOTO_DO;
}
return nSum;
}
没错,这个和do…while循环语句实现的功能相同,是一个循环,并且至少执行一遍。下面再来看主题:
23: int nSum = 0;
004272AE mov dword ptr [nSum],0
24: int nIndex = 0;
004272B5 mov dword ptr [nIndex],0
25: do
26: {
27: nSum += nIndex;
004272BC mov eax,dword ptr [nSum]
004272BF add eax,dword ptr [nIndex]
004272C2 mov dword ptr [nSum],eax
28: nIndex++;
004272C5 mov eax,dword ptr [nIndex]
004272C8 add eax,1
004272CB mov dword ptr [nIndex],eax
//先执行加操作
29: } while(nIndex <= nCount);
004272CE mov eax,dword ptr [nIndex]
004272D1 cmp eax,dword ptr [nCount]
004272D4 jle LoopDO+2Ch (4272BCh)
//跳转至do处
可以看到两段代码非常相似,但是仔细可以看出,if判定的条件和while判定的条件是相反的(这里指的是汇编代码的判定条件而不是C语言的代码),这样就很容易区分if语句与do…while之间的区别了。
由于循环结构的比较简单,所以就直接看例子,下面的也是。
36: while (nIndex <= nCount)
0042730C mov eax,dword ptr [nIndex]
0042730F cmp eax,dword ptr [nCount]
00427312 jg LoopWhile+48h (427328h)
//先进行判断
37: {
38: nSum += nIndex;
00427314 mov eax,dword ptr [nSum]
00427317 add eax,dword ptr [nIndex]
0042731A mov dword ptr [nSum],eax
39: nIndex++;
0042731D mov eax,dword ptr [nIndex]
00427320 add eax,1
00427323 mov dword ptr [nIndex],eax
40: }
00427326 jmp LoopWhile+2Ch (42730Ch)
//无条件跳转至while循环开始处
可以看到while循环需要进行两次跳转,如此一来效率必然比不上do…while循环。但是可以将其优化成如下模式:
If(xxx)
{
do
{
}while(xxx);
}
for循环是最常用的循环语句,同时也较为复杂。例子:
47: for (int nIndex = 0; nIndex <= nCount; ++nIndex)
00427365 mov dword ptr [nIndex],0
0042736C jmp LoopFor+37h (427377h)
//以上是赋初值部分,即nIndex = 0
0042736E mov eax,dword ptr [nIndex]
00427371 add eax,1
00427374 mov dword ptr [nIndex],eax
//步长计算部分,即++nIndex
00427377 mov eax,dword ptr [nIndex]
0042737A cmp eax,dword ptr [nCount]
0042737D jg LoopFor+4Ah (42738Ah)
//判断及跳转部分
48: {
49: nSum += nIndex;
0042737F mov eax,dword ptr [nSum]
00427382 add eax,dword ptr [nIndex]
00427385 mov dword ptr [nSum],eax
50: }
00427388 jmp LoopFor+2Eh (42736Eh)
//无条件跳转至步长计算部分
这里可以看到,for循环与while循环一样,都是先判断后执行,但是for循环共有3次跳转,是循环语句中效率最低的,但是如果把赋初值部分移动到循环之外即可转化为while循环然后再转化为do…while循环。
以上代码均为Debug版本的反汇编代码,Release版本的会按照后面讲解的优化方案进行优化,此处不再赘述。
例子:IDA Pro反汇编的Release版本
源代码:
// 代码外提
int CodePick(intnCount)
{
int nSum =0;
int nIndex =0;
do
{
nSum += nIndex;
nIndex++;
} while(nIndex< nCount - 1);
return nSum;
}
反汇编代码:
.text:00401000 arg_0 = dword ptr 8
.text:00401000
.text:00401000 push ebp
.text:00401001 mov ebp, esp
.text:00401003 mov edx, [ebp+arg_0]
.text:00401006 xor eax, eax
.text:00401008 xor ecx, ecx
.text:0040100A dec edx
.text:0040100B jmp short loc_401010
.text:0040100B ;---------------------------------------------------------------------------
.text:0040100D align 10h
.text:00401010
.text:00401010 loc_401010: ; CODE XREF:sub_401000+Bj
.text:00401010 ;sub_401000+15j
.text:00401010 add eax, ecx
.text:00401012 inc ecx
.text:00401013 cmp ecx,edx
.text:00401015 jl short loc_401010
可以很明显的看到.text:0040100A dec edx这条语句不在循环中,这就是代码外提。把不需要重复的计算都放到循环之外,这样可以减少计算量,提高计算效率。
源代码:
// 强度降低
void DoRate(int argc)
{
int t = 0;
int i = 0;
while (t { t = i * 99; i++; } printf("%d",t); } 这里只列出循环的反汇编代码: .text:00401030 loc_401030: ; CODE XREF: sub_401020+17j .text:00401030 mov eax, ecx .text:00401032 add ecx, 63h .text:00401035 cmp eax, edx .text:00401037 jl short loc_401030