59

在阅读其他人的代码时,我看到了一些非常奇怪for的循环。我一直在尝试为forC 中的循环搜索完整的语法解释,但这非常困难,因为“for”一词出现在不相关的句子中,使得搜索几乎不可能有效地谷歌搜索。

读完这篇文章后,我想到了这个问题,这让我再次感到好奇。

这里for

for(p=0;p+=(a&1)*b,a!=1;a>>=1,b<<=1);

中间条件有一个逗号分隔两段代码,这个逗号有什么作用?我理解右侧的逗号,因为它同时表示a>>=1b<<=1

但是在循环退出条件下,会发生什么?它是在何时p==0、何时a==1或两者都发生时退出?

如果有人能帮助我理解这一点,并可能为我指明完整for循环语法描述的方向,那就太好了。

4

6 回答 6

130

逗号不排除 for 循环;它是逗号运算符。

x = (a, b);

将首先执行 a,然后执行 b,然后将 x 设置为 b 的值。

for 语法是:

for (init; condition; increment)
    ...

这在某种程度上(暂时忽略continuebreak相当于:

init;
while (condition) {
    ...
    increment;
}

因此,您的 for 循环示例(再次忽略continueand break)等效于

p=0;
while (p+=(a&1)*b,a!=1) {
    ...
    a>>=1,b<<=1;
}

就像它一样(再次忽略continueand break):

p=0; 
while (true) {
    p+=(a&1)*b;
    if (a == 1) break;
    ...
    a>>=1;
    b<<=1;
}

for 循环的两个额外细节,它们不在上面对 while 循环的简化转换中:

  • 如果条件被省略,它总是true(导致无限循环,除非 a breakgoto,或其他东西打破循环)。
  • Acontinue就像在增量之前转到标签一样,不像continuewhile 循环中的 a 会跳过增量。

此外,关于逗号运算符的一个重要细节:它是一个序列点,例如&&and ||(这就是为什么我可以将它拆分为单独的语句并保持其含义完整的原因)。


C99 的变化

C99 标准引入了本解释前面未提及的一些细微差别(这对 C89/C90 非常有用)。

首先,所有循环本身就是块。有效地,

for (...) { ... }

本身包裹在一对大括号中

{
for (...) { ... }
}

标准说:

ISO/IEC 9899:1999 §6.8.5 迭代声明

¶5迭代语句是一个块,其范围是其封闭块范围的严格子集。循环体也是一个块,其范围是迭代语句范围的严格子集。

这也在基本原理中根据额外的大括号进行了描述。

其次,initC99 中的部分可以是(单个)声明,如

for (int i = 0; i < sizeof(something); i++) { ... }

现在“环绕循环的块”变成了它自己的;它解释了为什么i不能在循环外访问变量。您可以声明多个变量,但它们必须是同一类型:

for (int i = 0, j = sizeof(something); i < j; i++, j--) { ... }

标准说:

ISO/IEC 9899:1999 §6.8.5.3 for 声明

该声明

for ( clause-1 ; expression-2 ; expression-3 ) statement

行为如下: 表达式 expression-2 是在每次执行循环体之前计算的控制表达式。每次执行循环体后,表达式 expression-3 被评估为 void 表达式。如果clause-1 是一个声明,它声明的任何变量的范围是声明的其余部分和整个循环,包括其他两个表达式;它在控制表达式的第一次评估之前按照执行顺序到达。如果 Clause-1 是一个表达式,则在第一次计算控制表达式之前,它会被计算为一个 void 表达式。133)

子句 1 和表达式 3 都可以省略。省略的表达式 2 被非零常量替换。

133)因此,第 1 条规定了循环的初始化,可能声明一个或多个变量用于循环;控制表达式 expression-2 指定在每次迭代之前进行的评估,这样循环的执行将继续,直到表达式比较等于 0;而 expression-3 指定在每次迭代后执行的操作(例如递增)。

于 2008-11-09T21:47:03.267 回答
8

逗号只是分隔两个表达式,并且在 C 中允许正常表达式的任何地方都有效。这些是按从左到右的顺序执行的。最右边表达式的值是整个表达式的值。

for循环由三部分组成,其中任何部分也可以是空的;一个(第一个)在开始时执行,一个(第三个)在每次迭代结束时执行。这些部分通常分别初始化和增加一个计数器;但他们可以做任何事情。

第二部分是在每次执行开始时执行的测试。如果测试产生false,则中止循环。这里的所有都是它的。

于 2008-11-09T21:46:10.897 回答
5

C 风格的 for 循环由三个表达式组成:

for (initializer; condition; counter) statement_or_statement_block;
  • 初始化程序在循环开始时运行一次。
  • 在每次迭代之前检查条件。只要计算结果为真,循环就会运行。
  • 计数器在每次迭代后运行一次。

这些部分中的每一个都可以是在您编写循环所用的语言中有效的表达式。这意味着可以更有创意地使用它们。任何你想事先做的事情都可以进入初始化程序,你想做的任何事情都可以进入条件或计数器,直到循环不再有主体。

为此,逗号运算符非常方便。它允许您将表达式链接在一起以形成一个新的表达式。大多数情况下,它在 for 循环中以这种方式使用,逗号运算符的其他含义(例如值分配注意事项)起着次要作用。

即使你可以通过创造性地使用语法来做一些聪明的事情——我会保持清醒,直到我找到一个真正好的理由这样做。使用 for 循环玩代码高尔夫会使代码更难阅读和理解(和维护)。

维基百科也有一篇关于 for 循环的好文章

于 2008-11-09T21:57:58.580 回答
2

for循环中的所有内容都是可选的。我们可以初始化多个变量,可以检查多个条件,可以使用逗号运算符迭代多个变量。

以下for循环将带您进入无限循环。检查条件时要小心。

for(;;) 
于 2009-03-09T14:04:22.537 回答
0

Konrad 提到了我要重复的关键点:最右边的表达式的值是整个表达式的值。

当我在 for 循环的“条件”部分中放置两个测试时,Gnu 编译器会发出此警告

warning: left-hand operand of comma expression has no effect

我真正想要的“条件”是两个测试之间有一个“&&”。根据 Konrad 的说法,只有逗号右侧的测试会影响条件。

于 2010-04-13T18:11:15.937 回答
-1

for 循环是在特定时间执行 for(;;)

for 循环的语法

为了(;;)

或者

for(初始化器;条件;计数器)

例如 (rmv=1;rmv<=15;rmv++)

在 for 块中执行 15 次

1.首先初始化值,因为启动值

(例如)rmv=1 或 rmv=2

2.第二条语句是测试条件为真或假,条件为真执行for循环的次数,条件为假终止块,

例如 i=5;i<=10 条件为真

i=10;i<10 the condition is false terminate for block,

3.三是递增或递减

(例如)rmv++ 或 ++rmv

于 2014-10-14T11:15:07.350 回答