for 循环和while循环区别

原创 2015年11月11日 15:59:26

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

do-while循环

其通用形式是这样的:

do
    body-statement
while  (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-statement
while(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;

}

fact_while反汇编分析

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循环的不同组成部分如下:

syntax Expression
init-expr i = 2
test-expr i <= n
update-expr i ++
body-statement result *= i

  用这些部分带入前面给出的模板中的相应位置,得到下面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 参数下它们生成的汇编指令是一样的(看来这应该跟优化配置和编译器选择有很大关系)。
这里写图片描述

版权声明:本文为博主原创文章,转载需注明链接。 https://blog.csdn.net/XscKernel/article/details/49534745

while循环

额,我看了一篇博文,发现这篇确实不错,网址是: http://blog.csdn.net/zz3111057382/article/details/51916159 额,讲得比较简单,while循...
  • cnyali_ljf
  • cnyali_ljf
  • 2016-07-18 15:13:52
  • 1583

for循环与while循环的区别

/*  * 一个需求:使用for循环和while循环都可以去实现,那么到底两者之间有什么区别?  * 从内存角度考虑:  * 局部变量在栈内存中存在,当for循环语句结束,那么变量会及时被...
  • qq_39345059
  • qq_39345059
  • 2017-10-17 21:21:11
  • 1794

Java 三种循环的流程图画法总结(for,while,do-while循环)

1.  for循环           for循环形式: for(表达式1;表达式2;表达式3)           流程图:                        图1 for循环流程图 ...
  • evangel_z
  • evangel_z
  • 2011-12-02 15:57:48
  • 12181

java for循环的几种写法

J2SE 1.5提供了另一种形式的for循环。借助这种形式的for循环,可以用更简单地方式来遍历数组和Collection等类型的对象。本文介绍使用这种循环的具体方式,说明如何自行定义能被这样遍历的类...
  • wangyi_lin
  • wangyi_lin
  • 2011-11-07 22:35:09
  • 66245

1.2.3 Python中的for循环和while循环

For循环的基本结构是:for 循环规则:操作语句1,简单循环从基本结构看,其有着同if条件语句类似的地方:都有冒号;语句块都要缩进。这些是不可或缺的。&amp;gt;&amp;gt;&amp;gt;...
  • u011418530
  • u011418530
  • 2018-03-14 16:40:56
  • 134

Python学习笔记(While循环)

和其他编程语言一样,Python同样提供并支持循环语句。循环语句允许我们执行一条或多条语句多次。 Python中提供的循环语句有for循环和while循环.while循环是指在给定的条件成立时(tr...
  • jun_life
  • jun_life
  • 2016-06-18 17:06:21
  • 19699

JAVA-while循环语句

while循环语句用法比for语句用起来简单,格式也对的简单;while(判断条件){ 循环体 }public class WhileTest { public static vo...
  • qq_37131111
  • qq_37131111
  • 2017-01-17 10:14:14
  • 16201

while(true)循环与CPU占用率问题

一、为什么死循环占用CPU高 一个进程如果是死循环,那么占有的CPU会很高,可以操作系统时间片运行的,到了一定时间不是会自动切换到别的进程吗?既然即便是死循环,到时间还是会切换到别的进程,为什么占用...
  • zhuyijian135757
  • zhuyijian135757
  • 2014-12-30 17:54:09
  • 4128

蓝鸥Unity开发基础——While和DoWhile语句学习笔记

蓝鸥Unity开发基础——While和DoWhile语句学习笔记:循环结构、循环结构-条件满足时,反复执行同一个语句、循环结构的作用是重复执行一段代码、循环结构是有条件的,循环次数是有限的...
  • sinat_35761779
  • sinat_35761779
  • 2016-08-16 12:05:24
  • 1145
收藏助手
不良信息举报
您举报文章:for 循环和while循环区别
举报原因:
原因补充:

(最多只允许输入30个字)