1 while ( condition )
2 statement;
3 more_code();
在上述代码片段中,只要
condition
为
true
,就可以重复执行
statement
。在while循环的每次迭代中,
condition
都会被评估为
true
或
false
。如果是
false
,则while循环结束,并在其范围之外继续执行(在本例中,是第4行的
more_code()
)。
通常,我们习惯用花括号
{
和
}
将我们想要在循环中执行的代码部分括起来,但这不是强制性的。如果我们不这样做,循环代码将由单个语句组成,即紧随
while
部分后面的语句。
实际上,我们经常结合
while
和花括号括起来的
代码块,可以解释为提供此代码块
替代单个语句,括号提供信息,指示编译器(通过分析它与前面和后面的代码的关系)应该将该块视为单个语句
一样。
然而,由于提供单个语句而不是常见的代码块是完全有效的,因此值得了解有一个有效的空语句。我们通过在没有前导代码引起任何事情的情况下键入分号来获得空语句。因此,以下是完全有效的:
1 code;
2 ; // empty statement
3 ; // another empty statement
或者实际上是这样的:
1 code;; // a "code" statement followed by empty statement in the same line
while( condition )
部分没有以分号结尾,因此如果它应该控制一些实际代码(除了
condition
),那么它后面就不应该跟着分号。如果它紧接着被一个分号跟着,那么这个分号将构成(并且被编译器解释为)一个空语句,所以循环代码将是空的。如果这不是我们想要的,那么我们想要循环的代码,无论是代码块还是语句,都不会被循环,而是在循环结束后(如果循环结束了)执行一次。
1 int a = 0
2 while ( a < 3 )
3 a++
4 printf("This never happens.")
值得注意的是,在C语言中,行号只对我们人类重要。如果代码行数和缩进不符合程序员的意图,那么它们可能会误导我们,因为程序员未能编写按照他想要的方式运行的代码。
因此,在问题中的这两个片段中,我们会不断地评估
condition
直到其返回
false
。为了理解正在发生的事情,我们需要检查逗号操作符的工作方式。
(请注意,虽然逗号作为字符可以在C语言的许多不同位置用于完全不同的含义——例如函数声明、定义和调用——但在本例中,逗号字符是条件的一部分,因此它充当着类似于
+
或
%
操作符的东西。)
expression1 , expression2
逗号运算符会先评估`expression1`,然后是`expression2`,并返回`expression2`的值。
每次评估条件时,我们都会评估这两个表达式(在本例中为操作数`i++`和`i<=8`),然后将右侧表达式的值视为逗号运算本身的结果以及我们条件的值。因此只要右侧操作数解析为`true`,循环就会一直重复。
通常我们使用条件来控制循环的执行,但是像这种情况一样,条件可能具有“副作用”(有意或无意)。在我们的例子中,每次评估条件都会影响变量`i`:它会增加一个。
我们的示例仅在`condition`的操作数顺序上有所不同,因此请注意真正控制循环执行的右操作数。
让我们首先检查第二个示例。在这种情况下,我们有条件`i ++,i <= 8`。这意味着在每次评估时,我们首先增加`i`,然后检查它是否小于或等于8。因此,在对条件进行第一次评估时,我们将`i`从0增加到1,并得出1 <= 8,因此循环继续。构建的循环将在`i`变为9时中断,即在第9次迭代时。
现在对于第一个示例,条件为`i <= 8,++i`。由于比较没有副作用,也就是说,我们可以以任何顺序执行任意数量的比较,如果那是我们所做的唯一事情,也就是说,如果我们没有按结果有序或顺序执行任何其他操作,那些比较将什么也不做。就像在我们的例子中一样,我们评估`i<=8`,它会评估为`true`或`false`,但我们不使用这个结果,只是继续评估右操作数。因此左操作数根本无关紧要。另一方面,右操作数既具有副作用,又成为整个条件的值。在每个循环迭代之前,我们都检查`i ++`是否评估为`true`或`false`。
`i ++`是后增量的一元运算符。它返回`i`的值,然后将其增加一(`i ++`和`++ i`之间的差异在这种情况下微妙但至关重要)。因此,发生的情况是我们首先检查`i`是否为`true`或`false`,然后将`i`增加一。
在C中,没有`boolean`类型。如果整数具有非零值,则认为它们为`true`。
当我们对i++
进行第一次评估时,得到的是0,即false
。这意味着循环甚至没有进行一次迭代就被打破了。但是它并不会中断对i++
的评估,这会导致在我们完成循环并执行超出循环范围的代码之前,i
增加1。因此,一旦我们完成while循环,i
已经是1了。
如果我们想要非常准确地理解,在评估整个条件的结果之前,我们执行与此评估相关的任何代码后,时间上发生的部分是之后的。因此,我们首先记住在达到i++
部分时i
为0,然后将i
增加1,然后我们完成执行condition
,因此我们向决定是否应该执行另一个(在这种情况下是第一个)迭代或跳过循环部分并继续移动的代码提供值0。这正是为什么即使已经确定循环将结束,但在condition
完成执行之前,循环内的所有内容实际上都会发生的确切原因:已确定,但尚未检查和执行,直到condition
完成执行。