3.6 控制(3.6.5 用条件控制实现条件分支))
文章目录线索栏笔记栏1. 核心方法条件控制转移1方法2思想3图示图3-162. 标准翻译模板先判“假”3. 练习题3.16 解析短路求值与多个分支4. 练习题3.17 解析替代模板5. 练习题3.18 解析从汇编逆向C代码1目标2逆向推导过程总结栏线索栏核心方法将C语言的if-else语句翻译为机器代码最常用的方法是什么其背后的核心思想是什么“Goto代码”什么是“goto代码”为什么在教材中使用它它直接对应汇编中的什么结构标准模板C语言if-else语句翻译为控制流的通用汇编/“goto代码”模板是什么先判断真还是假短路求值C语言中逻辑与操作符的“短路”行为在机器级是如何实现的为什么一个C的if语句可能对应汇编中的多个条件分支练习题3.16替代模板if-else语句是否存在另一种等价的“goto代码”翻译模板练习题3.17教材为何选择先判断条件为“假”的模板逆向工程给定一段复杂的、包含多层条件跳转的汇编代码如何系统地逆向推导出原始的C语言条件逻辑练习题3.18笔记栏1. 核心方法条件控制转移1方法结合有条件跳转和无条件跳转指令来实现条件分支。2思想机器代码为then-statement和else-statement分别生成代码块然后利用条件判断控制CPU跳转到正确的代码块执行。3图示图3-16通过计算两数之差绝对值的函数 absdiff_se进行展示。1C代码 (a)标准if-else。2Goto代码 (b)用于理解汇编控制流的C语言形式。它使用goto和标号模拟跳转。3汇编代码 ©实际生成的机器级指令。其控制流与goto代码高度一致。2. 标准翻译模板先判“假”教材给出的、也是编译器最常用的翻译模式是先判断条件为“假”0然后跳转到else部分。// C语言if(test-expr)then-statementelseelse-statement// 对应的“Goto代码”/汇编控制流模板ttest-expr;if(!t)// 如果条件为假gotofalse;// 跳转到else部分then-statementgotodone;// 跳过else部分false:else-statement done:...优点逻辑清晰与“条件不满足时跳过”的直觉相符是编译器生成代码的常见模式。3. 练习题3.16 解析短路求值与多个分支1C代码if (p a *p) *p a;2汇编代码包含两次条件跳转je .L1和 jge .L1。3A. Goto版本voidcond_goto(longa,long*p){if(p0)// 对应 testq %rsi, %rsi; je .L1gotodone;// 短路如果p为NULL直接结束if(a*p)// 对应 cmpq %rdi, (%rsi); jge .L1gotodone;// 如果 a *p 不执行赋值*pa;// 对应 movq %rdi, (%rsi)done:return;}4B. 原因一个C的if语句包含两个条件p非空、a *p由连接。是“短路与”需要分别对两个条件依次求值。因此汇编代码中为每个条件的“不满足”情况p0或a*p都生成一个条件跳转以跳过赋值语句。这导致了两个条件分支。4. 练习题3.17 解析替代模板1A. 替代Goto版本先判“真”longgotodiff_alt_se(longx,longy){longresult;intt(xy);if(t)// 如果条件为真 (xy)gototrue;// else-statementge_cnt;resultx-y;gotodone;true:// then-statementlt_cnt;resulty-x;done:returnresult;}2B. 选择理由教材选择的模板先判“假”可能更优因为它将更常见或更重要的路径then-statement放在了“无跳转”的默认执行流中。在处理器流水线中不跳转通常比跳转性能更好避免分支预测错误惩罚。因此将期望更频繁执行的代码块放在需要if(!t)才跳转的地方可以提升性能。5. 练习题3.18 解析从汇编逆向C代码1目标根据test函数的汇编代码补全其C代码框架。2逆向推导过程1初始化leaq (%rdi,%rsi), %rax; addq %rdx, %rax→ val x y z2第一层条件 (if)cmpq $-3, %rdi; jge .L2→ 判断 x -3若是跳转到.L2外层else-if或之后。这意味着第一个条件是 x -3。3内层if-else在x -3的块内有cmpq %rdx, %rsi; jge .L3→ 判断 y z若是跳转到.L3内层else。这意味着内层条件是 y z。内层theny z时执行movq %rdi, %rax; imulq %rdi, %rax; imulq %rdx, %rax→ val x * x * z内层else(.L3)y z时执行movq %rsi, %rax; imulq %rdx, %rax→ val y * z4外层else-if当x -3时来到.L2。接着cmpq $2, %rdi; jle .L4→ 判断 x 2若是跳转到.L4直接返回。这意味着第二个条件是 x 2。外层else-if的thenx 2时执行movq %rdi, %rax; imulq %rdx, %rax→ val x * z5其他情况x -3且 x 2时直接返回初始值 xyz。补全的C代码longtest(longx,longy,longz){longvalxyz;if(x-3){if(yz)valx*x*z;elsevaly*z;}elseif(x2)valx*z;returnval;}总结栏本节深入剖析了高级语言条件分支结构到机器级条件跳转指令的映射是理解程序控制流的基础。核心模式if-else的机器级实现遵循“条件检测 → 条件跳转 →代码块选择”的固定模式。教材的标准模板先判断条件为假因其潜在的性能优势使常见路径免于跳转而被广泛采用。“Goto代码”是桥梁它是一种强大的思维工具和表述工具用于清晰揭示汇编代码的控制流逻辑是理解编译器输出和进行手工优化的关键。短路求值的代价C语言的和||操作符的短路行为在机器级体现为对每个子表达式进行独立的条件判断和跳转。一个复杂的条件表达式可能编译成多个条件分支指令。逆向工程能力练习题3.18综合训练了从复杂的、嵌套的条件跳转汇编中重建高级语言逻辑的能力。关键在于1逐条跟踪指令理解每个cmp和jXX构成的“条件-跳转对”。2画出控制流图明确各个代码块标号之间的跳转关系。3用高层次的条件逻辑if, else if, else来归纳底层的跳转集合。最终启示条件控制是程序的“决策大脑”。理解其编译方式不仅能读懂汇编更能深刻理解程序运行的代价分支预测错误会导致性能大幅下降这为后续学习第5章“优化程序性能”特别是关于分支预测的优化埋下了伏笔。同时逆向推导能力是调试、分析二进制程序和安全研究的基础技能。