逗号不排除 for 循环;它是逗号运算符。
x = (a, b);
将首先执行 a,然后执行 b,然后将 x 设置为 b 的值。
for 语法是:
for (init; condition; increment)
...
这在某种程度上(暂时忽略continue
)break
相当于:
init;
while (condition) {
...
increment;
}
因此,您的 for 循环示例(再次忽略continue
and break
)等效于
p=0;
while (p+=(a&1)*b,a!=1) {
...
a>>=1,b<<=1;
}
就像它一样(再次忽略continue
and break
):
p=0;
while (true) {
p+=(a&1)*b;
if (a == 1) break;
...
a>>=1;
b<<=1;
}
for 循环的两个额外细节,它们不在上面对 while 循环的简化转换中:
- 如果条件被省略,它总是
true
(导致无限循环,除非 a break
,goto
,或其他东西打破循环)。
- A
continue
就像在增量之前转到标签一样,不像continue
while 循环中的 a 会跳过增量。
此外,关于逗号运算符的一个重要细节:它是一个序列点,例如&&
and ||
(这就是为什么我可以将它拆分为单独的语句并保持其含义完整的原因)。
C99 的变化
C99 标准引入了本解释前面未提及的一些细微差别(这对 C89/C90 非常有用)。
首先,所有循环本身就是块。有效地,
for (...) { ... }
本身包裹在一对大括号中
{
for (...) { ... }
}
标准说:
ISO/IEC 9899:1999 §6.8.5 迭代声明
¶5迭代语句是一个块,其范围是其封闭块范围的严格子集。循环体也是一个块,其范围是迭代语句范围的严格子集。
这也在基本原理中根据额外的大括号进行了描述。
其次,init
C99 中的部分可以是(单个)声明,如
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 指定在每次迭代后执行的操作(例如递增)。