本章介绍以下内容:
关键字:for、while、do while
运算符:<、>、>=、<=、!=、==、+=、*=、-=、/=、%=
函数:fabs()
C语言有3种循环:for、while、do while
使用关系运算符构建控制循环的表达式
其他运算符
循环常用的数组
编写有返回值的函数
大多数人都希望自己是体格强健、天资聪颖、多才多艺的能人。虽然有时事与愿违,但至少我们用 C能写出这样的程序。诀窍是控制程序流。对于计算机科学(是研究计算机,不是用计算机做研究)而言,一门语言应该提供以下3种形式的程序流:
执行语句序列;
如果满足某些条件就重复执行语句序列(循环
通过测试选择执行哪一个语句序列(分支)。
读者对第一种形式应该很熟悉,前面学过的程序中大部分都是由语句序列组成。while循环属于第二种形式。本章将详细讲解while循环和其他两种循环:for和do while。第三种形式用于在不同的执行方案之间进行选择,让程序更“智能”,且极大地提高了计算机的用途。不过,要等到下一章才介绍这部分的内容。本章还将介绍数组,可以把新学的知识应用在数组上。另外,本章还将继续介绍函数的相关内容。首先,我们从while循环开始学习。
6.1 再探while循环
6.1.1 程序注释
这叫作伪代码(pseudocode),是一种用简单的句子表示程序思路的方法,它与计算机语言的形式相对应。伪代码有助于设计程序的逻辑。确定程序的逻辑无误之后,再把伪代码翻译成实际的编程代码。使用伪代码的好处之一是,可以把注意力集中在程序的组织和逻辑上,不用在设计程序时还要分心如何用编程语言来表达自己的想法。例如,可以用缩进来代表一块代码,不用考虑C的语法要用花括号把这部分代码括起来。
6.1.2 C风格读取循环
但是C更为简洁,下面的代码:
status = scanf("%ld", &num);
while (status == 1)
{
/* 循环行为 */
status = scanf("%ld", &num);
}
可以用这些代码替换:
while (scanf("%ld", &num) == 1)
{
/*循环行为*/
}
6.2 while语句
while循环的通用形式如下:
while ( expression )
statement
statement部分可以是以分号结尾的简单语句,也可以是用花括号括起来的复合语句。
6.2.1 终止while循环
6.2.2 何时终止循环
6.2.3 while:入口条件循环
while循环是使用入口条件的有条件循环。所谓“有条件”指的是语句部分的执行取决于测试表达式描述的条件
6.2.4 语法要点
缩进是为了让读者阅读方便,不是计算机的要求。
即使while语句本身使用复合语句,在语句构成上,它也是一条单独的语句。该语句从while开始执行,到第1个分号结束。在使用了复合语句的情况下,到右花括号结束。
要注意放置分号的位置。
测试条件后面的单独分号是空语句(null statement),它什么也不做。在C语言中,单独的分号表示空语句。有时,程序员会故意使用带空语句的while语句,因为所有的任务都在测试条件中完成了,不需要在循环体中做什么。
6.3 用关系运算符和表达式比较大小
while循环经常依赖测试表达式作比较,这样的表达式被称为关系表达式(relational expression),出现在关系表达式中间的运算符叫做关系运算符(relational operator)
表6.1 关系运算符
比较浮点数时,尽量只使用<和>。因为浮点数的舍入误差会导致在逻辑上应该相等的两数却不相等。
使用fabs()函数(声明在math.h头文件中)可以方便地比较浮点数,该函数返回一个浮点值的绝对值(即,没有代数符号的值)
6.3.1 什么是真
对C而言,表达式为真的值是1,表达式为假的值是0
6.3.2 其他真值
一般而言,所有的非零值都视为真,只有0被视为假。在C中,真的概念还真宽!
用while (goats)替换while (goats !=0)
待比较的一个值是常量,可以把该常量放在左侧有助于编译器捕获错误:
6.3.4 新的_Bool常量
在编程中,表示真或假的变量被称为布尔变量(Boolean variable),所以_Bool是C语言中布尔变量的类型名。_Bool类型的变量只能储存1(真)或0(假)。如果把其他非零数值赋给_Bool类型的变量,该变量会被设置为1。这反映了C把所有的非零值都视为真。
C99提供了stdbool.h头文件,该头文件让bool成为_Bool的别名,而且还把true和false分别定义为1和0的符号常量。包含该头文件后,写出的代码可以与C++兼容,因为C++把bool、true和false定义为关键字。
6.3.5 优先级和关系运算符
关系运算符的优先级比算术运算符(包括+和-)低,比赋值运算符高
关系运算符之间有两种不同的优先级。
高优先级组: <<= >>=
低优先级组: == !=
与其他大多数运算符一样,关系运算符的结合律也是从左往右。
表6.2 运算符优先级
小结:while语句
关键字:while
一般注解:
while语句创建了一个循环,重复执行直到测试表达式为假或0。while语句是一种入口条件循环,也就是说,在执行多次循环之前已决定是否执行循环。因此,循环有可能不被执行。循环体可以是简单语句,也可以是复合语句。
形式:
while ( expression )
statement
在expression部分为假或0之前,重复执行statement部分。
示例:
while (n++ < 100)
printf(" %d %d\n",n, 2 * n + 1); // 简单语句
while (fargo < 1000)
{ // 复合语句
fargo = fargo + step;
step = 2 * step;
}
小结:关系运算符和表达式
关系运算符:
每个关系运算符都把它左侧的值和右侧的值进行比较。
< 小于
<= 小于或等于
== 等于
>= 大于或等于
> 大于
!= 不等于
关系表达式:
简单的关系表达式由关系运算符及其运算对象组成。如果关系为真,关系表达式的值为 1;如果关系为假,关系表达式的值为0。
示例:
5 > 2为真,关系表达式的值为1
(2 + a) == a 为假,关系表达式的值为0
6.4 不确定循环和计数循环
所谓不确定循环,指在测试表达式为假之前,预先不知道要执行多少次循环。
计数循环(counting loop)。这类循环在执行循环之前就知道要重复执行多少次
在创建一个重复执行固定次数的循环中涉及了3个行为:
1.必须初始化计数器;
2.计数器与有限的值作比较;
3.每次循环时递增计数器。
6.5 for循环
关键字for后面的圆括号中有3个表达式,分别用两个分号隔开。第1个表达式是初始化,只会在for循环开始时执行一次。第 2 个表达式是测试条件,在执行循环之前对表达式求值。如果表达式为假(本例中,count大于NUMBER时),循环结束。第3个表达式执行
更新,在每次循环结束时求值。程序清单6.10用这个表达式递增count 的值,更新计数。完整的for语句还包括后面的简单语句或复合语句。for圆括号中的表达式也叫做控制表达式,它们都是完整表达式,所以每个表达式的副作用(如,递增变量)都发生在对下一个表达式求值之前
1.可以使用递减运算符来递减计数器
2.可以让计数器递增2、10等
3.可以用字符代替数字计数
4.还可以测试其他条件
5.可以让递增的量几何增长,而不是算术增长。
6.可以省略一个或多个表达式(但是不能省略分号),只要在循环中包含能结束循环的语句即可
7.第1个表达式不一定是给变量赋初值,也可以使用printf()。
小结:for语句
关键字:for
一般注解:
for语句使用3个表达式控制循环过程,分别用分号隔开。initialize表达式在执行for语句之前只执行一次;然后对test表达式求值,如果表达式为真(或非零),执行循环一次;接着对update表达式求值,并再次检查test表达式。for语句是一种入口条件循环,即在执行循环之前就决定了是否执行循环。因此,for循环可能一次都不执行。statement部分可以是一条简单语句或复合语句。
形式:
for ( initialize; test; update )
statement
在test为假或0之前,重复执行statement部分。
示例:
for (n = 0; n < 10 ; n++)
printf(" %d %d\n", n, 2 * n + 1);
6.6 其他赋值运算符:+=、-=、*=、/=、%=
并非一定要使用这些组合形式的赋值运算符。但是,它们让代码更紧凑,而且与一般形式相比,组合形式的赋值运算符生成的机器代码更高效。
6.7 逗号运算符
逗号运算符有两个其他性质。首先,它保证了被它分隔的表达式从左往右求值(换言之,逗号是一个序列点,所以逗号左侧项的所有副作用都在程序执行逗号右侧项之前发生)
整个逗号表达式的值是右侧项的值
小结:新的运算符
赋值运算符:
下面的运算符用右侧的值,根据指定的操作更新左侧的变量:
+= 把右侧的值加到左侧的变量上
-= 从左侧的变量中减去右侧的值
*= 把左侧的变量乘以右侧的值
/= 把左侧的变量除以右侧的值
%= 左侧变量除以右侧值得到的余数
示例:
rabbits *= 1.6;与rabbits = rabbits * 1.6;相同
这些组合赋值运算符与普通赋值运算符的优先级相同,都比算术运算符的优先级低。因此,
contents *= old_rate + 1.2;
最终的效果与下面的语句相同:
contents = contents * (old_rate + 1.2);
逗号运算符:
逗号运算符把两个表达式连接成一个表达式,并保证最左边的表达式最先求值。逗号运算符通常在for循环头的表达式中用于包含更多的信息。整个逗号表达式的值是逗号右侧表达式的值。
示例:
for (step = 2, fargo = 0; fargo < 1000; step *= 2)
fargo += step;
6.8 出口条件循环:do while
while循环和for循环都是入口条件循环,即在循环的每次迭代之前检查测试条件,所以有可能根本不执行循环体中的内容
出口条件循环(exit-condition loop),即在循环的每次迭代之后检查测试条件,这保证了至少执行循环体中的内容一次。这种循环被称为 do while循环
do
statement
while ( expression );
statement可以是一条简单语句或复合语句。注意,do while循环以分号结尾
小结:do while语句
关键字:do while
一般注解:
do while 语句创建一个循环,在 expression 为假或 0 之前重复执行循环体中的内容。do while语句是一种出口条件循环,即在执行完循环体后才根据测试条件决定是否再次执行循环。因此,该循环至少必须执行一次。statement部分可是一条简单语句或复合语句。
形式:
do
statement
while ( expression );
在test为假或0之前,重复执行statement部分。
示例:
do
scanf("%d", &number);
while (number != 20);
6.9 如何选择循环
要让for循环看起来像while循环,可以省略第1个和第3个表达式。例如:
for ( ; test ; )
与下面的while效果相同:
while ( test )
要让while循环看起来像for循环,可以在while循环的前面初始化变量,并在while循环体中包含更新语句。例如:
初始化;
while ( 测试 )
{
其他语句
更新语句
}
与下面的for循环效果相同:
for ( 初始化 ;测试 ; 更新 )
其他语句
当循环涉及初始化和更新变量时,用for循环比较合适,而在其他情况下用while循环更好
对于涉及索引计数的循环,用for循环更适合
6.10 嵌套循环
嵌套循环(nested loop)指在一个循环内包含另一个循环。嵌套循环常用于按行和列显示数据,也就是说,一个循环处理一行中的所有列,另一个循环处理所有的行
6.10.1 程序分析
6.10.2 嵌套变式
6.11 数组简介
数组(array)是按顺序储存的一系列类型相同的值
用于识别数组元素的数字被称为下标(subscript)、索引(indice)或偏移量(offset)。下标必须是整数,而且要从0开始计数
可以通过外层循环控制内层循环,在每次外层循环迭代时内层循环完成不同的任务
遵循模块化(modularity)的原则。模块化隐含的思想是:应该把程序划分为一些独立的单元,每个单元执行一个任务。这样做提高了程序的可读性。也许更重要的是,模块化使程序的不同部分彼此独立,方便后续更新或修改程序。在掌握如何使用函数后,可以把每个执行任务的单元放进函数中,提高程序的模块化。
6.12 使用函数返回值的循环示例
6.12.1 程序分析
6.12.2 使用带返回值的函数
6.13 关键概念
6.14 本章小结
本章的主题是程序控制。C语言为实现结构化的程序提供了许多工具。while语句和for语句提供了入口条件循环。for语句特别适用于需要初始化和更新的循环。使用逗号运算符可以在for循环中初始化和更新多个变量。有些场合也需要使用出口条件循环,C为此提供了do while语句。
典型的while循环设计的伪代码如下:
获得初值
while (值满足测试条件)
{
处理该值
获取下一个值
}
for循环也可以完成相同的任务:
for (获得初值; 值满足测试条件; 获得下一个值)
处理该值
这些循环都使用测试条件来判断是否继续执行下一次迭代。一般而言,如果对测试表达式求值为非0,则继续执行循环;否则,结束循环。通常,测试条件都是关系表达式(由关系运算符和表达式构成)。表达式的关系为真,则表达式的值为1;如果关系为假,则表达式的值为0。C99新增了_Bool类型,该类型的变量只能储存1或0,分别表示真或假。
除了关系运算符,本章还介绍了其他的组合赋值运算符,如+=或*=。这些运算符通过对其左侧运算对象执行算术运算来修改它的值。
接下来还简单地介绍了数组。声明数组时,方括号中的值指明了该数组的元素个数。数组的第 1 个元素编号为0,第2个元素编号为1,以此类推。例如,以下声明:
double hippos[20];
创建了一个有20个元素的数组hippos,其元素从hippos[0]~hippos[19]。利用循环可以很方便地操控数组的下标。
最后,本章演示了如何编写和使用带返回值的函数。