while循环需要明确的条件,而for循环不需要,为什么?

22
在C++中,你可以在for循环的条件表达式中使用一个空条件,例如for (;;)for (int x = 0;; ++x)。但是你不能这样做:while ()
当for循环中省略了条件表达式时,该条件被假定为true(因此循环将永远执行)。那么为什么while循环不是这种情况呢?为什么不允许while ()作为while (true)的别名呢?

8
答案可能有两个方面:1)为了与C99向后兼容,2)因为标准委员会这样规定了。使用while()可能存在一些语法原因,但从理论上讲,如果他们真的想要的话,那是可以修复的。 - Zac Howland
2
@ZacHowland 1) 那么问题是为什么在C99中会出现这种情况呢 ;) - leemes
2
@leemes - 因为C90标准委员会这么规定了 ;) - Zac Howland
2
这个问题并不基于观点,OP正在询问是否存在这种奇怪现象的原因。答案要么是“是的:因为X”,要么是“不是真的”。其中一个是正确的,另一个是错误的。这有什么主观性可言? - Mooing Duck
1
#define while(c) for(;c;) 这样"修复"它 ;) (开个玩笑,甚至不能正常工作) - leemes
显示剩余3条评论
6个回答

29

大概是因为for语句中的每个子句都是可选的副作用。有些for循环不需要赋值,有些不需要条件,还有些不需要增量。要求必须有一定数量的子句只会增加无谓的复杂性。


这解释了为什么 for(;;) 是有效的,但没有解决为什么 while() 不能(这可能是基于个人观点的)。 - Zac Howland
1
重点是他们并没有打算在控制语句中普遍地使条件成为可选项。这纯粹是在for循环的特殊情况下保持一致性的问题。 - Sneftel
@ZacHowland 唯一让 while() 在没有条件的情况下工作的原因是为了表达无限循环(或通过跳出来结束)的常见习语。由于 for(;;) 已经表达了这个习语,所以没有必要改变 while() 的语法。 - Jon Hanna
我的问题至少有三种解释,其中之一是标准规定的(6.8.5.3);是否存在并且如果存在,C标准中这个决定的历史原因是什么(当然,C++标准中包含它的历史原因是向后兼容性);而不管这些设计决策的历史原因是否也促使了语言设计者将它们包含在内,是否存在一个好的论据支持这些设计决策。由于第三个问题对我来说似乎最重要,而Sneftel的回答非常强大、有说服力和直观,我接受了这个答案。 - Luka Mikec

8
以下内容摘自书籍《深入C语言秘密》:
早期的 C 语言没有单独的 & 和 && 或 | 和 || 操作符。相反,它使用了从 B 和 BCPL 继承而来的“真值上下文”的概念:在需要布尔值的地方,如在“if”和“while”等语句后面,& 和 | 操作符被解释为现在的 && 和 ||;在普通表达式中,则使用按位解释。这种方式效果不错,但很难解释。(在真值上下文中有“顶层操作符”的概念。)
所以,while()会设置一个真值上下文,不能是空的或者省略,与 if() 的原因相同,早期语言的机制要求它们不能为空。

3
我不明白。如果表达式是“可选的”,那么运算符的含义和“上下文”与问题有什么关系?请为我解释一下。 - leemes
为了在C语言中实现“真值上下文”(如B和BCPL中),if()和while()后面需要一个布尔值...因此它们不能是空的。尽管原始原因(消除&作为&或&&等的含义的歧义)已经不存在,但该设计被传递到更新版本的C,然后传递到C ++。 - Nikos Athanasiou
3
还是不明白为什么这是个争论点。他们可以说,如果在括号内有某些内容,那么它就是一个布尔表达式上下文。另外,为什么对于“for”循环不适用同样的规则呢? - leemes
是的,他们本来可以这样做,但他们没有,这就是设计选择。你不需要理解其中任何内容,不要自找麻烦。我只是简单地介绍了它的历史,解释了在 while()if() 后面期望布尔值的原始原因。而 for 的情况则不同,因为有三个上下文存在,“声明 - 真值 - 修改”,而不是一个。 - Nikos Athanasiou

8
在C++中,您允许在for循环内部使用空条件...那为什么不允许while()成为while(true)的别名呢?
首先,C++(以及C#和许多其他语言)是为了与C向后兼容而采用了现有的形式,因此我们来谈论C。
让我们做出一些声明并得出结论。
1. for循环允许省略条件有充分的理由。 2. 同样的理由也适用于while循环。 3. while循环不允许省略条件;这是不一致的。 4. 在语句设计选择的一致性是C的一个重要因素。 5. 因此,必须有一个不显眼的好理由来解释这种不一致性;必须有一个好的理由来解释为什么while循环与for循环不一致。
而现在的问题是“那个不显眼的好理由是什么?”
但是该问题只有在上述逻辑链条正确时才有意义,而它并不正确。其中只有第3个声明是真实的!C中的for循环是一个糟糕的设计;它是冗余的,对新手来说很困惑,其词法约定与语言的其余部分不一致。它几乎没有为语言提供额外的表示能力,而是直接使用while循环。
没有答案可以回答您的问题;正确的回答是完全拒绝问题的前提。for循环似乎是合理的,只是因为它是一个40多年的错误设计,而我们大多数人都是在它的怪癖中长大的,所以它的怪癖现在是第二天性;这是熟悉感,而不是良好的设计。如果我们没有用过for循环长大,我们就不会添加它。

1
然而最近,已经添加了基于范围的for循环,这表明对设计用于表示迭代序列的循环有着明显的需求。它的语义可能有些复杂,但我认为这更多是C语言在抽象序列能力方面的限制,而不是一般情况下是否应该包含该功能的可取性问题。 - Sneftel
1
@Sneftel:哦,别误会了;我喜欢一个循环的想法,它可以抽象出迭代序列的操作。for循环的根本问题在于它的语法非常怪异。分号可以逻辑上被认为是顺序组合副作用运算符。当你在C中说A(); B(); C();时,这意味着按顺序组合三个函数调用的副作用。这就是除了在foreach循环中以外的所有地方的含义。 - Eric Lippert
这是一个很好的观点。如果他们将语法制定为 for(a)(b)(c) { ... },甚至是 for {a}{c}(b) {...},那会很有趣。毕竟,在某种意义上,ac 应该是块,而 b 则不应该是。 - Sneftel
1
@Sneftel:这是一个开始。但是大括号会与作用域混淆,这很不幸。我建议使用一种叫做单词的神奇发明来提高清晰度。通过用单词替换 ;,我们可以获得更多的清晰度。比如 do (initializer while expression by statement) statement -- do (node* p = head while p != NULL by p = p->next;)...,这符合 C 语法的精神,而不会过载 ; 的含义。 - Eric Lippert

3

我认为主要区别在于表达式和语句。

当然,在C语言中,这两者并没有严格区分,但我认为for循环是语句:

for (statement; expression; statement) {
    ...
}

表达式是布尔表达式,而语句是具有副作用的语句。当然,C语言不是半函数化的;你可以在中间表达式中放置副作用,但这不是惯用法。
然而,有时候你不需要一个表达式,而是需要在每次循环迭代时使用语句,因此中间表达式是可选的:
// Infinite counter
int i;
for (i = 0;;i++) {
   ...
}

这意味着根据定义,该表达式被视为始终为真。

另一方面,whileif只能采用表达式。如果省略该表达式,则不会剩下什么非常有用的东西(不像还剩下的两个有用语句),没有人会写成:

// Idiot coding
if (true) {
   ...
}

因为 if 语句的目的是检查某个未知条件是否为真!

while 同样如此。 while 的目的是在表达式形式的条件为真时一遍又一遍地执行某些操作。这有点像“迭代的 if”。这就是为什么

while() {
   ...
}

被认为不合逻辑,因此不允许。


1
根据文档所述: 6.8.5.3 for语句

语句for (clause-1; expression-2; expression-3)的行为如下:

  1. 表达式expression-2是控制表达式,在执行循环体之前对其进行评估。在每次执行循环体之后,将表达式expression-3作为void表达式进行评估。如果clause-1是声明,则它声明的任何标识符的范围是剩余的声明和整个循环,包括其他两个表达式;在第一次评估控制表达式之前按执行顺序到达。如果clause-1是表达式,则在第一次评估控制表达式之前将其作为void表达式进行评估。158)
  2. 可以省略clause-1和expression-3。 省略expression-2将被替换为非零常数。
因此,在 for 循环中,条件将被替换为非零常量,而对于 while 循环没有这样的实现。

OP希望了解标准中此文本的原因;我假设他已经了解C/C++语法。 - anatolyg
好的。原始问题基本上是,为什么在for语句的第二个表达式中允许省略条件,但在while语句的条件中不允许?他已经知道规则,他想知道编写这些代码的人使用的推理。 - Sneftel

-3
while() 和 for(;;) 循环中使用的逻辑语句的区别在于:
  • 对于 for,该语句定义了循环退出的条件
  • 而对于 while(),该语句定义了执行循环的条件
假设你看到为进入循环,至少需要一个执行条件,而不是退出条件。

3
for循环的语法包含一个执行循环的条件,就像while循环的语法一样。 - anatolyg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接