C语言编程中的vs.while?


78

有三个环在C: ,forwhiledo-while。它们之间有什么区别?

例如,似乎几乎所有while语句都可以被for语句替换,对吗?那么,使用的好处是什么while


9
您忘记了条件GOTO循环。尽管人们不认为它是一个循环,但我相信所有循环本质上都可以编译为条件GOTO循环。
阿姆斯特朗(Armstrongest)2010年

1
递归是否也被视为循环?
Nyan 2010年

写一个在x86 isa中将简单的for循环编译为更有效的loop指令(减法,比较和单指令跳转)的编译器比编译while成该loop指令的编译器难吗?当前的编译器完全可以编译forloop指令吗?
西罗Santilli郝海东冠状病六四事件法轮功2013年

Answers:


166

一个while循环总是首先评估条件。

while (condition) {
  //gets executed after condition is checked
}

做/ while循环会一直执行在代码do{}块,然后再评估条件。

do {
  //gets executed at least once
} while (condition); 

一个for循环允许你启动一个计数器变量,检查条件和方式来增加您的柜台都在一行。

for (int x = 0; x < 100; x++) {
   //executed until x >= 100
}

归根结底,它们仍然是循环,但是它们在执行方式方面提供了一定的灵活性。

这是对使用每种不同类型的循环背后的原因的很好的解释,这可能有助于清除问题。由于clyfe

for和之间的主要区别在于while语用上的问题:我们通常for在已知迭代次数while时使用,而在事先不知道迭代次数时使用构造。该whileVSdo ... while 问题也是语用学,第二执行一次在启动的指令,之后它的行为就像简单的一段时间。


for循环特别简洁,因为它们非常简洁。为了这个for循环:

for (int x = 0; x < 100; x++) {
   //executed until x >= 100
}

要编写为while循环,您必须执行以下操作:

int count = 0;
while (count < 100) {
  //do stuff
  count++;
}

在这种情况下,还有更多的东西要跟上,这count++;可能会在逻辑上迷失方向。最终可能会很麻烦,具体取决于在哪里count递增,以及是否应该在循环逻辑之前或之后递增。使用for循环时,计数器变量始终在循环的下一次迭代之前增加,这会增加代码的一致性。


为了完整起见,在这里讨论breakcontinue语句可能很有意义,这些语句在执行循环处理时会派上用场。

break将立即终止当前循环,并且不再执行任何迭代。

//will only run "do stuff" twice
for (int x = 0; x < 100; x++) {
  if (x == 2) {
    break;
  }
  //do stuff
}

继续将终止当前迭代并继续进行下一个迭代

//will run "do stuff" until x >= 100 except for when x = 2
for (int x = 0; x < 100; x++) {
  if (x == 2) {
    continue;
  }
  //do stuff
}

请注意,在for循环中,continue计算;的part3表达式for (part1; part2; part3)。相反,在while循环中,它只是跳转以重新评估循环条件。


7
您是否应该讨论“继续”的行为?在“ for”循环与“ while”循环中?否则,很好的答案。
乔纳森·莱夫勒

3
+1为出色的答案。值得注意的是:for循环不需要计数器变量。在C风格的语言中,它们实际上是为(<start statement>; <continuation>; <每次迭代后执行>)。恰好是最常见的用法是计数器变量:for(int x = 0 [start]; x <100 [continue?]; ++ x [post-iteration])
Sean Edwards 2010年

10
for's和之间的主要区别在于while's语用上的问题:通常for已知迭代次数的情况下使用,而在事先不知道迭代次数的情况下使用while构造。该VS问题也是语用学,第二执行指令一旦在启动,之后它的行为就像简单。whiledo ... whilewhile
clyfe 2010年

2
+1精心编写的答案,有时重
访

1
我喜欢这个答案,尤其是您谈到在“ for”情况下增加计数器的一致性,而在“ while”情况下可能在迭代的任何地方增加计数器的一致性。老实说,尽管这里没有太多新内容,但我永远不会这么雄辩地说
Shravan 2010年

16

如果强烈关注速度和性能,最好的方法是在汇编级别验证编译器生成的代码。

例如,以下代码显示“ do-while”要快一些。这是因为“ do-while”循环未使用“ jmp”指令。

顺便说一句,在此特定示例中,最糟糕的情况由“ for”循环给出。:))

int main(int argc, char* argv[])
{
    int i;
    char x[100];

    // "FOR" LOOP:
    for (i=0; i<100; i++ )
    {
        x[i] = 0;
    }

    // "WHILE" LOOP:
    i = 0;
    while (i<100 )
    {
        x[i++] = 0;
    }

    // "DO-WHILE" LOOP:
    i = 0;
    do
    {
        x[i++] = 0;
    }
    while (i<100);

    return 0;
}

//“ FOR”循环:

    010013C8  mov         dword ptr [ebp-0Ch],0
    010013CF  jmp         wmain+3Ah (10013DAh)

  for (i=0; i<100; i++ )
  {
      x[i] = 0;
    010013D1  mov         eax,dword ptr [ebp-0Ch]  <<< UPDATE i
    010013D4  add         eax,1
    010013D7  mov         dword ptr [ebp-0Ch],eax
    010013DA  cmp         dword ptr [ebp-0Ch],64h  <<< TEST
    010013DE  jge         wmain+4Ah (10013EAh)     <<< COND JUMP
    010013E0  mov         eax,dword ptr [ebp-0Ch]  <<< DO THE JOB..
    010013E3  mov         byte ptr [ebp+eax-78h],0
    010013E8  jmp         wmain+31h (10013D1h)     <<< UNCOND JUMP
  }

//“ WHILE”循环:

  i = 0;
  010013EA  mov         dword ptr [ebp-0Ch],0
  while (i<100 )
  {
      x[i++] = 0;
    010013F1  cmp         dword ptr [ebp-0Ch],64h   <<< TEST
    010013F5  jge         wmain+6Ah (100140Ah)      <<< COND JUMP
    010013F7  mov         eax,dword ptr [ebp-0Ch]   <<< DO THE JOB..
    010013FA  mov         byte ptr [ebp+eax-78h],0
    010013FF  mov         ecx,dword ptr [ebp-0Ch]   <<< UPDATE i
    01001402  add         ecx,1
    01001405  mov         dword ptr [ebp-0Ch],ecx
    01001408  jmp         wmain+51h (10013F1h)      <<< UNCOND JUMP
  }

//“ DO-WHILE”循环:

i = 0;
.  0100140A  mov         dword ptr [ebp-0Ch],0
  do
  {
      x[i++] = 0;
    01001411  mov         eax,dword ptr [ebp-0Ch]   <<< DO THE JOB..
    01001414  mov         byte ptr [ebp+eax-78h],0
    01001419  mov         ecx,dword ptr [ebp-0Ch]   <<< UPDATE i
    0100141C  add         ecx,1
    0100141F  mov         dword ptr [ebp-0Ch],ecx
    01001422  cmp         dword ptr [ebp-0Ch],64h   <<< TEST
    01001426  jl          wmain+71h (1001411h)      <<< COND JUMP
  }
  while (i<100);

10

为了可读性


3
为此,您还可以将goto用作循环。
WhirlWind 2010年

另一个问题是递归。您可以用任何其他样式替换任何循环样式。
WhirlWind 2010年

10
实际上,所有循环实际上只是花哨的GOTO分支。
阿姆斯特朗

@WhirlWind除了在没有递归的语言中,您可能最终会出现堆栈溢出。(您可能最终会在这里询问堆栈溢出问题,这会让我有些微笑。)
Sean Edwards 2010年

@Sean是的,我正在考虑这个问题,是否有办法解决堆栈溢出问题。当然,如果有尾巴递归……
WhirlWind 2010年

10

它们都是可以互换的。您可以选择一种类型,而永远只使用一种类型,但是通常对于给定任务而言,一种类型更方便。就像说“为什么要切换,您只能使用一堆if语句”一样-的确如此,但是如果它是一种常见的模式来检查变量的一组值,那么它具有语言功能,并且很容易阅读要做到这一点


4
并不是的。for和while都可以执行身体零次-do-while无法执行。

4
@Neil do {if(!cond) break; /* do stuff */ } while(cond);。丑陋且重复,但这是我关于为何存在差异的观点:)
Michael Mrozek 2010年

@Neil:您可以使用其他标志来跳过第一次执行。可互换性并没有说明它多么容易或有用。;)
确保2010年

3
循环的主体仍处于输入状态-一会儿和一会儿不是这种情况。

2
@Neil好吧,关于“循环体”的某些定义。无论哪种方式,它都会导致相同的行为-/* do stuff */如果cond为假,它不会运行零件
Michael Mrozek 2010年

8

如果您希望在条件为真时执行循环,而不是进行一定数量的迭代,那么让其他人更容易理解:

while (cond_true)

比这样的事情:

for (; cond_true ; )

我不知道容易得多。我会猜测,如果C没有第一种形式,那么到现在我们都已经习惯了看到第二种形式。
caf 2010年

足够公平,但是如果我看到我正在维护的任何代码的第二个版本,我保证我会以为“ WTF”……
Justin Ethier 2010年

2
代码应读作散文,前者更接近自然语言。也许不是“容易得多”,但很可能会更快地识别和理解。
FredCooke

5

请记住,for循环本质上是花式while循环。他们是同一回事。

while <some condition is true> {
   // do some stuff
   // possibly do something to change the condition
}


for ( some var, <some condition is true>; increment var ) {

}

for循环的优点在于,意外地进行无限循环比较困难。或者更确切地说,当您执行一个操作时更明显,因为通常将循环变量放在初始语句中。

一个while循环更加清晰,当你没有做一个标准的递增模式。例如:

int x = 1;
while( x != 10 ) {
  if ( some condition )
     x = 10;
  else 
     x += 5;
}

6
但是,for()和将其作为while()写入之间有一个微妙的区别:您更改了continue的行为。对于for()情况,continue在检查条件之前执行递增步骤,而在while()中,continue将直接跳回条件。
拉普(Rup)2010年

是的,很好。另外,我认为人们忘记的是,一旦发生故障,所有循环都将编译为GOTO语句。
阿姆斯特朗

我认为这就是为什么while循环更经常导致无限循环bug的原因-可以在某个位置继续跳过跳过增量步骤。
Michael Mathews 2010年

@Michael:是的,非常正确。for循环可能是为了方便和有效的方法而引入的。
阿姆斯特朗

@Rup一个非常好的观点。我一直认为它是语法糖,但是继续行为意味着它不是真的。
JeremyP 2010年

4

您应该使用这样的循环,使其最完全符合您的需求。例如:

for(int i = 0; i < 10; i++)
{
    print(i);
}

//or

int i = 0;
while(i < 10)
{
    print(i);
    i++;
}

显然,在这种情况下,“ for”看起来比“ while”更好。如果在检查循环条件之前必须已经执行了某些操作,则应使用“ do while”。

对不起,我的英语不好)。


4

我见过的while/for循环的一个常见误解是它们的效率不同。While循环和for循环同样有效。我记得我的一位高中计算机老师告诉我,当您必须增加一个数字时,for循环对于迭代更有效。事实并非如此。

For循环只是语法上加糖的 while循环,使迭代代码的编写速度更快。

当编译器获取您的代码并对其进行编译时,它会将其转换为计算机更容易理解并在较低级别(汇编)上执行的形式。在此翻译过程中,whilefor语法之间的细微差别将丢失,并且它们变得完全相同。


“对于循环,句法只是简单地在while循环中加糖”:正如上面的注释之一已指出的那样,这并不完全正确。for中的for (i = 0 i < limit ; i++)
Continue

3

for建议使用索引或在此方案的变体的固定迭代。

Awhiledo... while是在每次都必须检查条件时使用的构造(除了某些类似索引的构造,请参见上文)。它们的不同之处在于首次执行条件检查的时间。

您可以使用任何一种构造,但是它们根据使用情况各有优缺点。


3

我前段时间注意到,For循环通常比while循环生成更多的机器指令。但是,如果您仔细查看反映我的观察结果的示例,则区别在于两到三个机器指令,几乎没有值得考虑的地方。

还要注意,可以通过将WHILE循环烘焙到代码中来消除其初始化程序,例如:

static int intStartWith = 100;

静态修改器将初始值烘焙到代码中,从而保存(鼓乐曲)一条MOV指令。更重要的是,将变量标记为静态将其移出堆栈框架。变量对齐允许的话,它也可能产生稍小的代码,因为MOV指令及其操作数比例如整数,布尔值或字符值(ANSI或Unicode)占用更多的空间。

但是,如果变量在8个字节的边界上对齐,则通用默认设置,代码中的int,bool或TCHAR都将与MOV指令占用相同的字节数。


2

他们所做的工作都是一样的。您可以使用其中任何一个执行相同的操作。但是在可读性,可用性,便利性等方面,它们有所不同。


2

while和do-while之间的区别在于,while检查循环条件,如果为true,则执行主体并再次检查条件。do-while在执行主体后检查条件,因此do-while会至少执行一次主体。

当然,您可以将while循环编写为do-while和vv,但这通常需要一些代码重复。


2

的一个特殊之处do while是,您需要等待一会儿才能使用分号。它通常用于宏定义中,以一次执行多个语句,同时限制宏的影响。如果将宏定义为块,则可能会发生某些解析错误。

一种解释等


1
这是可怕的hackish虽然,这不是一个很大的原因do-while存在
迈克尔Mrozek

不,我从没说过,但这是一种很特别的用法,它很普遍。
LB40

@Michael是的,这是一种hack,但是它是一种非常常用的方法,它使包含多个语句的宏在所有语法上下文中的行为完全符合预期。
JeremyP 2010年

@JeremyP我知道,我也使用过它,但它并不是真正的用例do-while,它只是被使用,因为它碰巧创建了一个块并且最后仍然需要用分号
Michael Mrozek 2010年

我只是想指出do-while的这种特殊用法。问题的一部分是,有什么区别……那是一个区别。(但我同意,这是
个小问题

2

For循环(至少考虑C99)优于while循环,因为它们限制了递增变量的范围。

当条件取决于某些输入时,“ while循环”很有用。在三种循环类型中,它们很少使用。


2

在for和while之间: while不需要初始化也不需要update语句,因此它看起来更好,更优雅;for可能缺少两个或全部一条语句,因此如果您需要初始化,循环条件和循环前的“更新”,它是最灵活,最明显的。如果只需要循环条件(在循环开始时进行了测试),那就while更优雅了。

在for / while和do-while之间:在do-while条件中在循环末尾求值。如果循环必须至少执行一次,则更为舒适。


2

虽然更灵活。在适用的情况下,FOR更简洁。

FOR非常适合具有某种计数器的循环,例如

for (int n=0; n<max; ++n)

当然,正如其他人指出的那样,您可以使用WHILE完成相同的事情,但是现在初始化,测试和增量已经跨越了三行。如果循环的主体较大,则可能会出现三行分开的行。这使读者更难了解自己在做什么。毕竟,尽管“ ++ n”是FOR中非常常见的第​​三部分,但肯定不是唯一的可能性。我编写了很多循环,在其中编写了“ n + = increment”或更复杂的表达式。

当然,FOR也可以与计数器以外的其他设备配合使用。喜欢

for (int n=getFirstElementFromList(); listHasMoreElements(); n=getNextElementFromList())

等等。

但是当“下一次循环”逻辑变得更加复杂时,FOR崩溃了。考虑:

initializeList();
while (listHasMoreElements())
{
  n=getCurrentElement();
  int status=processElement(n);
  if (status>0)
  {
    skipElements(status);
    advanceElementPointer();
  }
  else
  {
    n=-status;
    findElement(n);
  }
}

也就是说,如果根据处理过程中遇到的条件,前进的过程可能不同,则FOR语句是不切实际的。是的,有时您可以使它使用足够复杂的表达式,使用三元?:运算符等来工作,但这通常会使代码的可读性降低,而不是更具可读性。

在实践中,我的大多数循环都是逐步遍历某种数组或某种结构,在这种情况下,我将使用FOR循环。或者正在从数据库中读取文件或结果集,在这种情况下,我使用WHILE循环(“ while(!eof())”或类似的东西)。


如果for标头变长,则只需在分号后分割行。这不是while而不是for的论点。
klutt

@klutt涉及嵌套三元运算符和六级括号或相当程度的复杂性的冗长且复杂的表达式本身的问题并不在于该行太长而无法放入编辑器窗口中,而是变得难以阅读和理解。
杰(Jay)

是的,但这与您选择哪种循环无关。
klutt

@klutt当然可以。如果“下一个”操作涉及具有许多条件的复杂逻辑,则不能轻易将其放置在FOR循环中。您可能必须具有带三元嵌套或类似结构的复杂表达式,或者必须从FOR语句中取出下一个并将其放入循环主体中。此时,您还可以编写一个WHILE循环。
杰伊,

2

除了do-while循环外,它们几乎相同。for当您有counter一种变量时,循环是好的。这很明显。while循环在检查标志的情况下很有意义,如下所示:

while (!done) {
   if (some condtion) 
      done = true;
}


0

/ * while循环

5块钱

1巧克力= 1块钱

while my money is greater than 1 bucks 
  select chocolate
  pay 1 bucks to the shopkeeper
  money = money - 1
end

回家,不能去商店,因为我的钱= 0美元* /

#include<stdio.h>
int main(){
  int money = 5;

  while( money >= 1){   
    printf("inside the shopk and selecting chocolate\n");
    printf("after selecting chocolate paying 1 bucks\n");
    money = money - 1 ;  
    printf("my remaining moeny = %d\n", money);
    printf("\n\n");
  }

  printf("dont have money cant go inside the shop, money = %d",  money);

  return 0;
} 

无限金钱

while( codition ){ // condition will always true ....infinite loop
  statement(s)
}

请访问此视频以更好地了解 https://www.youtube.com/watch?v=eqDv2wxDMJ8&t=25s

/ * for循环

5块钱

for my money is greater than equal to 1 bucks   0      money >= 1
  select chocolate
  pay 1 bucks to the shopkeeper
  money = money - 1  1-1 => 0
end

* /

#include<stdio.h>
int main(){

  int money = 5;
  for( ; money >= 1; ){     0>=1  false 
    printf("select chocolate \n");
    printf("paying 1 bucks to the shopkeeper\n");
    money = money - 1;    1-1 = 0
    printf(" remaining money =%d\n", money);
    printf("\n\n");
  }

  return 0;
}

为了更好地理解,请访问https://www.youtube.com/watch?v=_vdvyzzp-R4&t=25s

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.