3.2 循环

在前面,我们遇到了遍历域的for语句,在我们的介绍性示例中,它声明了一个变量i,该变量被设置为值01234。这个语句的一般形式是:

038-02

这个语句其实很笼统。通常,clause1是一个赋值表达式或变量定义。它用于声明循环域的初始值。condition2测试循环是否应该继续。然后,expression3修改clause1中使用的循环变量。它在每次循环结束时执行。以下是一些建议:

  • 因为我们希望在for循环的上下文中严格定义循环变量(参见要点2.11),所以在大多数情况下,clause1应该是一个变量定义。
  • 因为for有四个不同的部分,相对比较复杂,而且从直观上不容易理解,所以statement-or-block通常应该是{ ... }块。

让我们再来看几个例子:

038-03

第一个fori10减到1。条件仍然是计算变量i的值,不需要针对值0进行多余的测试。当i变为0时,它将被计算为false,循环将停止。第二个for声明了两个变量istop。和前面一样,i是循环变量,stop是我们在条件中比较的对象,当i大于或等于stop时,循环终止。

第三个for看起来会一直走下去,但实际上是从9减到0。事实上,在下一章中,我们将看到C中的“大小”(类型为size_t的数字)永远不会为负数[练习2]

注意,这三个for语句都声明了名为i的变量。只要它们的作用域不重叠,那么这三个同名的变量就可以很好地共存。

在C语言中还有两个循环语句,whiledo

039-01

下面的示例展示了第一种方法的典型用法。它实现了所谓的Heron近似值来计算一个数x的倒数1/x。

039-02

只要给定条件的计算结果为true,它就会循环。do循环非常相似,只是它检查依赖块之后的条件:

039-03

这意味着如果条件的计算结果为falsewhile循环将根本不运行其依赖块,而do循环将在终止之前运行一次依赖块。

for语句一样,在dowhile中建议使用{ ... }块的变体。两者之间还有一个微妙的语法区别:do始终需要一个;while(条件)之后终止语句。稍后,我们将看到这是一个语法特性,在多个嵌套语句的上下文中非常有用。参见10.2.1节。

通过使用breakcontinue语句,这三个循环语句变得更加灵活。break语句不需要重新计算终止条件或者执行break语句后面的依赖块部分就停止循环:

039-04

这样我们就可以把a*x的计算,停止条件的计算,以及x的更新区分开来,while条件就变得无关紧要了。也可以用for来完成同样的事情,在C程序员中有这样一个传统来将其写成:

040-01

for(;;)在这里相当于while(true)。事实上,for的控制表达式(在;;中间的部分)可以省略,可被解释为“总是true”,这只是C规则中的一个历史产物,没有其他特殊的用途。

continue语句的使用频率较低。与break类似,它跳过依赖块其余部分的执行,因此在continue之后的块中的所有语句都不会在当前循环中执行。但是,如果条件为真,则重新计算条件并从依赖块的开始处继续执行:

040-02

在这些例子中,我们使用了一个标准的宏fabs,它来自tgmath.h头文件[1]。它计算一个double类型的绝对值。清单3.1是一个完整的程序,它实现了同样的算法,其中fabs被几个与某些固定数字的显式比较所取代:例如,eps1m24定义为1 - 2-24,或eps1p24定义为1 + 2-24。我们将在后面(5.3节)看到常量0x1P-24和类似定义的常量是如何工作的。

在第一阶段,将当前观察的数a与当前估计的数x的乘积分别与1.50.5进行比较,然后将x乘以0.52,直到乘积接近1。然后,在第二次循环中看是否接近代码中所示的Heron近似值,并以高精度来计算倒数。

程序的主要任务是计算在命令行上提供的所有数字的倒数。程序执行的样子如下:

040-03

为了处理命令行上的数字,该程序使用stdlib.h中的另一个库函数strtod[练习3] [练习4] [练习5]

挑战1 顺序排序算法

你会使用合并排序(利用递归)和快速排序(利用递归)对具有双精度或字符串排序键的数组进行排序吗?

如果不知道程序是否正确,你将一无所获。因此,你能提供一个简单的测试程序来检查结果数组是否真的排序了吗?

这个测试程序应该只扫描一次数组,并且应该比排序算法快得多。

清单3.1 计算数的乘法逆

041-01

[1]tgmath”代表泛类型数学函数。

[练习2]试着想象一下,当i的值为0并且通过运算符--递减时会发生什么。

[练习3]通过添加对中间值xPrintf调用来分析清单3.1。

[练习4]描述清单3.1中参数argcargv的作用。

[练习5]打印出eps1m01的值,然后观察当你对它们进行微调之后的输出。