众所周知,任何程序都可以由三种基本控制结构组成,分别是循序结构,选择结构,循环结构。
这三种结构翻译成汇编语言又是怎样的呢?这里主要考虑的是debug版本。对于release版本经过各种优化后结果不一样,不作考虑。这里的编译器采用的是Visual Studio 2008
顺序结构没什么悬念,这里就不提了,首先看下选择结构。
选择结构,主要有两种表现方式:if{ }else if{ } else{ }与 switch{case : case : default:}
首先来看下
if (a > 0 && b < 0) 00182DCC cmp dword ptr [a],0 ;两个判断,不合规范就跳到下一个else if处 00182DD0 jle foo+51h (182DF1h) 00182DD2 cmp dword ptr [b],0 00182DD6 jge foo+51h (182DF1h) { ;没跳走,执行代码块的内容 printf("if (a > 0 && b < 0)"); 00182DD8 mov esi,esp 00182DDA push offset string "if (a > 0 && b < 0)" (185974h) 00182DDF call dword ptr [__imp__printf (1882B4h)] 00182DE5 add esp,4 00182DE8 cmp esi,esp 00182DEA call @ILT+450(__RTC_CheckEsp) (1811C7h) 00182DEF jmp foo+87h (182E27h) ;执行完,跳出if } else if (a < 0) ;还是判断,不合规范就跳到下一个else 00182DF1 cmp dword ptr [a],0 00182DF5 jge foo+70h (182E10h) { printf("else if (a < 0)"); 00182DF7 mov esi,esp 00182DF9 push offset string "else if (a < 0)" (1857A8h) 00182DFE call dword ptr [__imp__printf (1882B4h)] 00182E04 add esp,4 00182E07 cmp esi,esp 00182E09 call @ILT+450(__RTC_CheckEsp) (1811C7h) } else 00182E0E jmp foo+87h (182E27h) ;我这得这条语句放在前一个else if里头更合适 { printf("else"); 00182E10 mov esi,esp 00182E12 push offset string "else" (1857A0h) 00182E17 call dword ptr [__imp__printf (1882B4h)] 00182E1D add esp,4 00182E20 cmp esi,esp 00182E22 call @ILT+450(__RTC_CheckEsp) (1811C7h) }
随便写的一个if循环,合理安排if的比较会让代码更少,但为了演示,也无所谓了。代码分析完也觉得简单,就是一开始看有点麻烦。
cmp <条件> ;多少个条件就多少个判断跳转 jle <下一个分支> ;这里通常与C/C++里的判断相反 …… cmp <条件> jle <下一个分支> (代码块) jmp <if外> ;最后一个if(else)代码块没有这条
switch(a) 00E82DC5 mov eax,dword ptr [a] 00E82DC8 mov dword ptr [ebp-0D0h],eax 00E82DCE cmp dword ptr [ebp-0D0h],0 ;判断跳转很频繁,首先考虑是switch 00E82DD5 je foo+4Bh (0E82DEBh) 00E82DD7 cmp dword ptr [ebp-0D0h],1 00E82DDE je foo+52h (0E82DF2h) ;这些是符合条件的就跳转到对应的代码块 00E82DE0 cmp dword ptr [ebp-0D0h],2 00E82DE7 je foo+5Bh (0E82DFBh) 00E82DE9 jmp foo+64h (0E82E04h) ;没有符合条件的,跳到 default { case 0: a = 0; 00E82DEB mov dword ptr [a],0 ;这里没有break,继续往下执行 case 1: a =1; 00E82DF2 mov dword ptr [a],1 break; 00E82DF9 jmp foo+6Bh (0E82E0Bh) ;break,跳出switch case 2: a =2; 00E82DFB mov dword ptr [a],2 break; 00E82E02 jmp foo+6Bh (0E82E0Bh) default: a =3; 00E82E04 mov dword ptr [a],3 }
连续的比较与条件跳转,容易让人联想到switch 对于代码块也比较简单 有break会增加一个无条件跳转
接下来看下循环结构,
循环结构主要有三种:For循环,While循环,Do-While循环。
至于其他语言的一些repeat until等不作考虑,请自行分析。
For循环
for (int i = 0;i< 5;i++) 00DB17CE mov dword ptr [i],0 00DB17D5 jmp foo+30h (0DB17E0h) 00DB17D7 mov eax,dword ptr [i] 00DB17DA add eax,1 00DB17DD mov dword ptr [i],eax 00DB17E0 cmp dword ptr [i],5 00DB17E4 jge foo+53h (0DB1803h) { printf("%d",i); 00DB17E6 mov esi,esp 00DB17E8 mov eax,dword ptr [i] 00DB17EB push eax 00DB17EC push offset string "%d" (0DB573Ch) 00DB17F1 call dword ptr [__imp__printf (0DB82B4h)] 00DB17F7 add esp,8 00DB17FA cmp esi,esp 00DB17FC call @ILT+450(__RTC_CheckEsp) (0DB11C7h) 00DB1801 jmp foo+27h (0DB17D7h) }
for(第一部分;第二部分;第三部分) { 循环体; }
mov <循环变量>,<初始值> ;第一部分。给循环变量赋初值 jmp B ;跳到第一次循环处,执行第二部分 A: (改动循环变量) ;第三部分。修改循环变量 B: cmp <循环变量>,<限制变量> ;第二部分。检查循环变量 jge 跳出循环 ;这里的判断条件通常与for中看到的相反 …… (循环体) …… jmp A ;跳回去第三部分,修改变量循环
while(a > 0) 00852DC5 cmp dword ptr [a],0 ;首先判断,不合条件跳出while代码块 00852DC9 jle foo+36h (852DD6h) { a--; 00852DCB mov eax,dword ptr [a] 00852DCE sub eax,1 00852DD1 mov dword ptr [a],eax } 00852DD4 jmp foo+25h (852DC5h) ;强制跳转会开头的判断
框架很简单,先判断,不符合条件就跳出代码块,否则继续执行下去,代码块最后跳回来继续判断。
A: cmp <循环变量>,<限制变量> jge B ;跳出代码块 (循环体) …… jmp A ;往回跳 B: ;循环结束
do { a--; 01362DC5 mov eax,dword ptr [a] 01362DC8 sub eax,1 01362DCB mov dword ptr [a],eax }while(a > 0); 01362DCE cmp dword ptr [a],0 01362DD2 jg foo+25h (1362DC5h)
Do - while就更加简单了,直接把判断挪到代码块后面。
A: cmp <循环变量>,<限制变量> jge B (循环体) …… jmp A B: ;循环结束
好了,基本介绍完了,此次只是为了告诫我们,在汇编里面,代码的理解跟高级语言是有点出入的,得转换下思维。另外,也说明了同一种逻辑可以通过多种方式来表达。
另外,这只是debug版本下的反汇编代码,在release版本下,代码将千变万化,比如,switch将可能会使用跳转表等来实现,部分if将直接被优化掉,毕竟使用流水线速度将会大大加快,忽然一个跳转将会打断流水线。
原文地址:http://blog.csdn.net/epluguo/article/details/38590411