C语言提供了好几种循环结构,即while、for和do-while。汇编语言中并没有相应的指令存在,作为替代,将条件测试和跳转组合起来实现循环的效果。大多数汇编器根据一个循环的do-while形式来产生循环代码,即使在实际程序中这种形式用的相对较少。其它的循环会首先转换成do-while形式,然后再编译成机器代码。

do-while循环

其通用形式是这样的:

do body-statementwhile (test-expr);

循环的效果就是重复执行body-statement,对test-expr求值,如果求值的结果为非零,就继续循环。注意,body-statement至少执行一次。

do-while的通用形式可以翻译成如下所示的条件和goto语句:

loop:

 body-statement t = test-expr; if(t) goto loop;

也就是说每次循环程序会执行循环体里面的语句,然后执行测试表达式。如果测试为真,则回去再执行一次循环。

下面示例用do-while循环计算函数参数的阶乘,写作n!只计算n>0时候n阶乘的值:

int fact_do(int n){ int result = 1; do { result *= n; n = n - 1; }while(n > 1); return result;}

汇编代码是do-while循环的一个实现形式,这里用的gcc编译器

gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)

编译参数是

$ gcc -m32 -O2 -o fact

因为非常不习惯AT&T汇编形式,所以这里用IDA pro 对得到的fact文件进行反汇编分析,原文的汇编形式(用edx保存参数n)我无论怎么调节参数都无法得到。图中是一个do-while循环的标准实现,eax初始化为1,epb+8地址处保存着参数n,0x08048404 处把参数n减一,紧接着0x08048408 处把n与1比较。如果为真则在0x0804840C处跳回循环的开始,这里是循环的关键地方由它来判断循环是继续还是退出。

综合0x080483F3,0x080483FA我们可以看到eax被初始化为1,在0x080483FD被乘法更新。如果学过x86汇编语言就知道 mul 乘法指令是离不开eax寄存器的,而且返回值通常也用eax寄存器。所以这里eax对应于结果result是无悬念的。

理解产生的汇编代码与原始代码之间的关系,关键是找到程序值和寄存器之间的映射关系。对于循环fact_do来说,这个任务非常简单,但是对于更复杂的程序来说,就可能是更具挑战性的任务。C语言编译器常常会重组计算,因此有些C代码中的变量在机器代码中没有对应的值;而有时,机器代码中又会引入源代码中不存在的新值。此外编译器还常常试图将多个程序值映射到一个寄存器上,来最小化寄存器的使用率。 上面的fact_do的过程对于逆向工程循环来说,是一个通用的策略。看看在循环之前如何初始化寄存器,在循环中如何更新和测试寄存器,以及在循环之后又如何使用寄存器。这些步骤中的每一步都提供了一个线索,组合起来就可以解开谜团。做好准备,你会看到令人惊奇的变换,其中有些情况很明显是编译器能够优化的代码,而有些情况很难解释编译器为什么要选用那些奇怪的策略。

while循环

while语句的通用形式如下:

while(test-expr) body-statement

与do-while不同的是,它对test-expr求值,在第一次执行body-statement之前,循环就可能中止。将while循环翻译成机器代码有很多种方法。一种常见的方法,也就是GCC采用的方法,是使用条件分支,在需要时省略循环体的第一次执行,从而将代码转换成do-while循环,如下:

if(!test-expr) goto done;do  body-statementwhile(test-expr);done:

接下来这个代码可直接翻译成goto代码,如下:

 if t = test-expr if(!t) goto done;loop: body-statement t = test-expr; if(t) goto loop;done:

使用这种策略,编译器常常会优化最开始的测试,比如说认为总是满足测试条件。

举个例子fact_while是使用while循环的阶乘函数的实现,这个函数能正确的计算 0!=1 。fact_while_goto是GCC产生的汇编代码的C语言翻译,比较fact_do 和fact_while 我们看到它们几乎是相同的。将while循环转换成do-while循环,以及将后者翻译成goto代码。

int fact_while(int n){ int result = 1; while(n > 1){ result *= n; n = n - 1; } return result;}
int fact_while_goto(int n){ int result = 1; if(n <= 1) goto done; loop: result *= n; n = n - 1; if(n > 1) goto loop; done: return result;}

for循环

for循环的通用形式如下

for(init-expr;test-expr;update-expr) body-statement

C语言标准说明,这样一个循环的行为与下面这段使用while循环代码的行为一样:

init-expr;while(test-expr) { body-statement update-expr;}

程序首先对初始表达式init-expr求值,然后进入循环;在循环中它先对测试条件test-expr求值,如果测试结果为“假”就会退出,否则执行循环体body-statement;最后对更新表达式update-expr求值。

这段代码编译后的形式,基于前面讲过的从while到do-while的转换,首先给出do-while的形式:

init-expr;if(!test-expr) goto done;do{ body-statement update-expr;}while(test-expr);done:

然后将它转换成goto代码:

 init-expr; t = test-expr if(!t) goto done;loop: body-statement update-expr; t = test-expr; if(t) goto loop;done:

作为一个示例,考虑用for循环写的阶乘函数:

int fact_for(int n){ int i; int result = 1; for(i = 2;i <= n; i ++) result *= i; return result;}

如上述代码所示,用for循环编写阶乘函数最自然的方式就是将从2一直到n的因子乘起来,因此这个函数与我们使用while或者do-while循环的代码都不一样。

这段代码中for循环的不同组成部分如下:

用这些部分带入前面给出的模板中的相应位置,得到下面goto代码的版本:

int fact_for_goto(int n){ int i = 2; int result = 1; if( !(i <=n ) ) goto done; loop: result *= i; i ++; if(i <= n) goto loop; done: return result;}

确实仔细查看GCC产生的汇编代码会发现非常接近如下形式:

综上所述,C语言中三种形式的所有循环— do-while,while和for–都可以用一种简单的策略来翻译,产生包含一个或多个条件分支的代码。控制的条件转移为循环翻译成机器代码提供了基本机制。

死循环选择for还是while

最后再说一下,看到有人在网上讨论死循环用 for(;;); 好,还是用 while(1); 好。

自己亲自测试了下,在 -O2 参数下它们生成的汇编指令是一样的(看来这应该跟优化配置和编译器选择有很大关系)。

c语言双重for循环流程图_for 循环和while循环区别相关推荐

  1. python中for循环流程图_Python流程控制-while循环-for循环

    写重复代码 是可耻的行为 -------------- 完美的分割线 -------------- 程序在一般情况下是按顺序执行的,编程语言提供了各种控制结构,允许更复杂的执行路径. 循环(loop) ...

  2. 数据流程图中的几种循环画法

    C语言编程中常用的三种循环为for(::),while  和 do-while. 1.  for循环 for循环形式: for(表达式1:表达式2:表达式3) 流程图: 图1 for循环流程图 2.  ...

  3. c语言双重for循环流程图_第九章、C语言循环之for

    上一章我们讲了while和do...while循环,这一章我们来学习一下C语言中的另外一种循环结构:for循环. 我们先来看一下for循环的结构: for(expression1:expression ...

  4. java双重for循环流程图_Java的for循环

    Java有好几种循环语句. for循环是Java的循环之一. for循环在java中用于重复执行一些语句,直到条件返回false. for循环有以下三个部分. 初始化 循环条件 自增或自减 for循环 ...

  5. java双重for循环流程图_JAVA程序逻辑中的循环结构

    在上一篇文章中提到了程序逻辑通过顺序.分支.循环三种结构来实现的,并介绍了分支结构的执行语句,那么今天这篇文章介绍实现程序逻辑的另一种结构--循环结构. 什么是循环结构 循环结构是指在程序设计语言中按 ...

  6. c语言双重性,C语言双重循环应用初探

    [摘要]循环结构是结构化程序设计中的三大基本控制结构之一,也是程序员学习程序设计时最为难以理解的部分.在C语言中,循环结构主要是由for语句和while语句以及do-while语句实现,其中for语句 ...

  7. c语言双重循环教程,C语言教程之如何进行循环的嵌套详细程序实例说明

    循环的嵌套 一个循环体内又包含另一个完整的循环结构称为循环的嵌套.内嵌的循环中还可以嵌套循环,这就是多层循环. 三种循环(while循环.do-while循环和for循环)可以互相嵌套. 下面几种都是 ...

  8. java双重for循环流程图_java 流程执行 循环 foreach循环

    一. if分支 1. 结构  if  else if   else 2.执行原则 if  if  if 结构  会一直去执行()里的判断语句 if else if  else if 结构  只要一条( ...

  9. go语言打印日期_go语言基础:流程控制(4)-多重循环跳转控制

    1.6 多重循环控制 1.6.1 概念解析 1)多重循环就是指将一个循环嵌套在另一个循环体内,就形成了嵌套循环.在外边的for循环称之为外层for循环,在里面的for循环称之为内层for循环.2)实质 ...

最新文章

  1. ppt格式刷快捷键_普通人 | 4个我在知乎上都没有见过的PPT实用技巧
  2. 作者:冯仕政(1974-),男,中国人民大学社会与人口学院教授、副院长。
  3. html 脚本 gdi,基于gdi的简单画图
  4. 2020年最新-Java集合面试题
  5. torch.ones理解
  6. 《团队-科学计算器-项目总结》
  7. python创建django项目语句_Python3—创建Django项目
  8. 视频教程-太空大战游戏实战课程-其他
  9. 股市心理学中的精神分析
  10. Android USB HID整理
  11. svg 绘制多边形渐变边框并填充渐变背景
  12. 小米2016AP2594计算机参数,小米6完整配置参数信息一览:6GB内存 双摄骁龙835
  13. 《暗时间》 读书感悟
  14. windows查看某个端口占用情况
  15. 如何在arxiv上面发论文
  16. python内置函数format的使用方法 python format函数怎么用
  17. GIC , SPI , PPI (窝窝科技的文章题目改了下)【转】
  18. 国内外SNS比较分析
  19. selenium webdriver 原理概述
  20. PFR简介---Cisco技术

热门文章

  1. Trunk的概述与配置方法(理论与是实践)
  2. 看20遍还觉得很搞笑之麦兜故事片段之鱼丸粗面
  3. 肌肉骨骼康复学-习题-单选
  4. 苹果备忘录怎么调字体大小_苹果备忘录误删了怎么恢复?快速恢复,只要收藏这两招!...
  5. 就业技术书文件表格_就业申请书范文
  6. 关于FreeRTOS中FromISR的使用
  7. 解决IMX6从 uboot调试usb start 命令无法识别U盘的问题
  8. 如何删除iphone中的文稿与数据
  9. Q345D钢材与Q355D钢材的不同
  10. Crap-Api接口管理系统部署