5.3 用if构成的多分支流程(1)
5.1节和5.2节介绍了由if与if…else…组成的分支结构。本节将介绍它们的组合形式—多分支结构。多分支结构类似于if…else…的组合方式,在if…else…的else之后再添加一个else if进行二次比较,这样就可以进行多次比较,再次选择程序流程,形成了多分支流程。它的C++(www.cppentry.com)语法格式为:if…else if…else if…,可重复后缀为else if。当最后为else时,便到了多分支结构的末尾处,不可再分支。通过代码清单5-6可以查看多分支结构的组成。
代码清单5-6 多分支结构—Debug版
- // C++(www.cppentry.com)源码说明:多分支结构
- void IfElseIf(int argc){
- if (argc > 0){ // 判断函数参数argc是否大于0
- printf("argc > 0"); // 比较成功后执行printf("argc > 0");
- }else if (argc == 0){ // 判断函数参数argc是否等于0
- printf("argc == 0"); // 比较成功后执行printf("argc == 0");
- }else{ // 前两次比较都失败,则此条语句被执行
- printf("argc <= 0");
- }
- }
-
- // C++(www.cppentry.com)源码与对应汇编代码讲解
- // C++(www.cppentry.com)源码对比
- if (argc > 0)
- ; if比较转换
- 00401108 cmp dword ptr [ebp+8],0
- ; 使用JLE条件跳转指令,如果判断后的结果小于等于0,则跳转到地址0x0040111D
- 0040110C jle IfElseIf+2Dh (0040111d)
- {
- printf("argc > 0");
- ; printf函数讲解略
- 0040110E push offset string "argc > 0" (00420f9c)
- 00401113 call printf (00401150)
- 00401118 add esp,4
- }else if (argc == 0)
- ; 对应else,当上一条if语句被执行,执行JMP指令,跳转到地址0x0040113F处
- ; 该地址为多分支结构结束地址,即最后一个else 或 else if的结束地址
- 0040111B jmp IfElseIf+4Fh (0040113f)
- ; if比较转换,使用条件跳转指令JNE,不等于0则跳转到地址0x00401132
- 0040111D cmp dword ptr [ebp+8],0
- 00401121 jne IfElseIf+42h (00401132)
- {
- printf("argc == 0");
- ; printf函数讲解略
- 00401123 push offset string "argc == 0" (0042003c)
- 00401128 call printf (00401150)
- 0040112D add esp,4
- }else
- ; 跳转到多分支结构的结束地址
- 00401130 jmp IfElseIf+4Fh (0040113f)
- {
- printf("argc <= 0");
- ; 注意,此处无判定。当以上各个条件均不成立时,以下代码则无条件执行
-
- ; 可将此处定义为最后的else块
- 00401132 push offset string "argc != 0" (00420030)
- 00401137 call printf (00401150)
- 0040113C add esp,4
- }
- 0040113F pop edi
代码清单5-6给出了if…else if…else 的组合。从代码中可以分析出,每条if语句由cmp和jxx组成,而else由一个jmp跳转到分支结构的最后一个语句块结束地址所组成。由此可见,虽然它们组合在了一起,但是每个if和else又都是独立的,if仍然是由CMP/TEST加jxx所组成,我们仍然可以根据上一节介绍的知识,利用jxx和jmp识别出if和else if语句块的边界,jxx指出了下一个else if的起始点,而jmp指出了整个多分支结构的末尾地址以及当前if或者else if语句块的末尾。最后的else块的边界也很容易识别,如果发现多分支块内的某一段代码在执行前没有判定,即可定义为else块,如上述代码中的00401132地址处。
总结:
- ; 会影响标志位的指令
- jxx ELSE_IF_BEGIN ; 跳转到下一条else if语句块的首地址
- IF_BEGIN:
- …… ; if语句块内的执行代码
- IF_END:
- jmp END ; 跳转到多分支结构的结尾地址
- ELSE_IF_BEGIN: ; else if 语句块的起始地址
- ; 可影响标志位的指令
- jxx ELSE_BEGIN ; 跳转到else分支语句块的首地址
- …… ; else if语句块内的执行代码
- IF_ELSE_ END: ; else if 结尾处
- jmp END ; 跳转到多分支结构的结尾地址
- ELSE_BEGIN: ; else语句块的起始地址
- …… ; else语句块内的执行代码
- END: ; 多分支结构的结尾处
- ……
如果遇到这样的代码块,需要考察各跳转指令之间的关系。当每个条件跳转指令的跳转地址之前都紧跟JMP指令,并且它们跳转的地址值都一样时,可视为一个多分支结构。JMP指令指明了多分支结构的末尾,配合比较判断指令与条件跳转指令,可还原出各分支语句块的组成。如果某个分支语句块中没有判定类指令,但是存在语句块,且语句块的位置在多分支语句块范围内,可以判定其为else块。