为什么’continue’语句忽略’while’循环中的循环计数器增量,而不是’for’循环?
如果我在while
循环中使用continue
,为什么它会进入无限循环,但在for
循环中工作正常?
循环计数器增量i++
在while
循环中被忽略,如果我在continue
之后使用它,但如果它在for
循环中它可以工作。
如果continue
忽略后续语句,那么为什么不忽略for
循环的第三个语句,其中包含计数器增量i++
? 在循环体之后执行for
循环的第三个语句时,不是continue
之后的for
循环的第三个语句并且应该被忽略吗?
while(i<10) //causes infinite loop { ... continue i++ ... } for(i=0;i<10;i++) //works fine and exits after 10 iterations { ... continue ... }
因为continue
回到循环的开始。 使用for
,后操作i++
是循环控制的一个组成部分,并在循环体重启之前执行。
有了这些, i++
只是循环体中的另一个陈述(与a = b
没什么不同),如果你在到达它之前continue
,就会跳过。
原因是continue
语句会使循环体中跟随它的语句短路。 由于你编写while
循环的方式在continue
语句后面有increment语句,因此它会被短路。 您可以通过更改while
循环来解决此问题。
许多教科书声称:
for (i = 0; i < N; ++i) { /*...*/ }
相当于:
i = 0; while (i < N) { /*...*/ ++i; }
但是,实际上,它真的像:
j = 0; while ((i = j++) < N) { /*...*/ }
或者,更迂腐:
i = 0; if (i < 10) do { /*...*/ } while (++i, (i < 10));
这些更相同,因为现在如果while
的主体有一个continue
,增量仍然会发生,就像在for
。 后一替代方案仅在迭代完成后执行增量,就像(前者在迭代之前执行增量,延迟将其保存在i
直到迭代之后)。
你的增量是在继续之后,所以它永远不会被执行
while(i<10) //causes infinite loop { ......... continue i++ ...... }
在任何循环中,继续将执行移回到循环的顶部, 而不是在continue语句之后执行任何其他指令 。
在这种情况下,for循环的定义总是执行(按标准C),而i ++; 语句未执行,因为它在continue语句之后出现。
因为for的第三部分总是被执行。
continue
语句将控件跳转到当前循环迭代语句的末尾,即它跳过当前迭代中语句的执行并移动到循环的下一次迭代。
使用while
循环, continue
语句使控制到达语句的结尾(包括递增语句),从而导致循环永远继续。
使用for
循环, continue
语句将控件跳转到语句的结尾并执行递增语句(在for
循环中,递增语句被认为与循环体内写入的语句分开)。
for循环保存条件语句和递增,所以当条件满足时,它继续执行for循环中的语句,但是如果写继续语句,它将再次到达for循环的第一行,即增加和检查条件语句,如果满足而不是再来执行。 对于while循环,它只检查条件语句,如果条件满足,它将用于执行while循环中的语句。 所以继续不会在它之后执行任何行。因此你的条件每次都满足并且进入无限循环。
如果满足循环的条件,则continue
绕过块的其余部分并再次从块的顶部开始。
接下来的问题是:“那我该怎么办?” 我能想到两个答案。
例:
void foo () { size_t i = 0; do { /*...*/ if ( /*...*/ ) { /*...*/ continue; } /*...*/ i++; } while ( /* loop conditional */ ); }
解决方案#1:手动增量
void foo () { size_t i = 0; do { /*...*/ if ( /*...*/ ) { /*...*/ i++; continue; } /*...*/ i++; } while ( /* loop conditional */ ); }
解决方案#2: goto
唯一有效的应用*
void foo () { size_t i = 0; do { /*...*/ if ( /*...*/ ) { /*...*/ goto foo_next; } /*...*/ foo_next: i++; } while ( /* loop conditional */ ); }
goto
在这种情况下有效,因为两个地方的增量在技术上是相同的指令。 当每次迭代 – 易变量变量更复杂时,此解决方案尤其重要; 例如,设置多个变量或使用等式或函数修改值。
在单个递增或递减声明的情况下,解决方案#1可能certificate是有利的; 但是,应该注意:如果在这样的实现之后修改代码,则必须记住更新语句的两个实例(这可能容易出错,特别是如果修改发生在一段延长的时间之后** )。 因此,我高度推荐解决方案#2。
*有些人考虑使用goto
不良做法。 我建议你自己决定,然后离开你: google for“c goto bad”
**提醒这种必要性的注释可能就足够了,但是 – 如果我的建议已被遵循 – 每次迭代 – 易变量变量仅限于一个语句。 我引述:
没有理由对单行进行评论
-Linus Torvalds(来源:http://yarchive.net/comp/linux/coding_style.html)