- 内容提要
- 前言
- 第 1 章 预备知识
- 第 2 章 开始学习 C++
- 第 3 章 处理数据
- 第 4 章 复合类型
- 第 5 章 循环和关系表达式
- 第 6 章 分支语句和逻辑运算符
- 第 7 章 函数——C++的编程模块
- 第 8 章 函数探幽
- 第 9 章 内存模型和名称空间
- 第 10 章 对象和类
- 第 11 章 使用类
- 第 12 章 类和动态内存分配
- 第 13 章 类继承
- 第 14 章 C++中的代码重用
- 第 15 章 友元、异常和其他
- 第 16 章 string 类和标准模板库
- 第 17 章 输入、输出和文件
- 第 18 章 探讨 C++新标准
- 附录 A 计数系统
- 附录 B C++保留字
- 附录 C ASCII 字符集
- 附录 D 运算符优先级
- 附录 E 其他运算符
- 附录 F 模板类 string
- 附录 G 标准模板库方法和函数
- 附录 H 精选读物和网上资源
- 附录 I 转换为 ISO 标准 C++
- 附录 J 复习题答案
5.2 while 循环
while 循环是没有初始化和更新部分的 for 循环,它只有测试条件和循环体:
首先,程序计算圆括号内的测试条件(test-condition)表达式。如果该表达式为 true,则执行循环体中的语句。与 for 循环一样,循环体也由一条语句或两个花括号定义的语句块组成。执行完循环体后,程序返回测试条件,对它进行重新评估。如果该条件为非零,则再次执行循环体。测试和执行将一直进行下去,直到测试条件为 false 为止(参见图 5.3)。显然,如果希望循环最终能够结束,循环体中的代码必须完成某种影响测试条件表达式的操作。例如,循环可以将测试条件中使用的变量加 1 或从键盘输入读取一个新值。和 for 循环一样,while 循环也是一种入口条件循环。因此,如果测试条件一开始便为 false,则程序将不会执行循环体。
程序清单 5.13 使用了一个 while 循环。该循环遍历字符串,并显示其中的字符及其 ASCII 码。循环在遇到空值字符时停止。这种逐字符遍历字符串直到遇到空值字符的技术是 C++处理 C-风格字符串的标准方法。由于字符串中包含了结尾标记,因此程序通常不需要知道字符串的长度。
程序清单 5.13 while.cpp
图 5.3 while 循环的结构
下面是该程序的运行情况:
verticalized 和 ASCIIized 并不是真正的单词,甚至将来也不会是单词。不过它们确实在输出中添加了一种“可爱”的氛围。
程序说明
程序清单 5.13 中的 while 条件像这样:
它可以测试数组中特定的字符是不是空值字符。为使该测试最终能够成功,循环体必须修改 i 的值,这是通过在循环体结尾将 i 加 1 来实现的。省略这一步将导致循环停留在同一个数组元素上,打印该字符及其编码,直到强行终止该程序。导致死循环是循环最常见的问题之一。通常,在循环体中忘记更新某个值时,便会出现这种情况。
可以这样修改 while 行:
经过这种修改后,程序的工作方式将不变。这是由于 name[i]是常规字符,其值为该字符的编码—非零值或 true。然而,当 name[i]为空值字符时,其编码将为 0 或 false。这种表示法更为简洁(也更常用),但没有程序清单 5.13 中的表示法清晰。对于后一种情况,“笨拙”的编译器生成的代码的速度将更快,“聪明”的编译器对于这两个版本生成的代码将相同。
要打印字符的 ASCII 码,必须通过强制类型转换将 name[i]转换为整型。这样,cout 将把值打印成整数,而不是将它解释为字符编码。
不同于 C-风格字符串,string 对象不使用空字符来标记字符串末尾,因此要将程序清单 5.13 转换为使用 string 类的版本,只需用 string 对象替换 char 数组即可。第 16 章将讨论可用于标识 string 对象中最后一个字符的技术。
5.2.1 for 与 while
在 C++中,for 和 while 循环本质上是相同的。例如,下面的 for 循环:
可以改写成这样:
同样,下面的 while 循环:
可以改写成这样:
for 循环需要 3 个表达式(从技术的角度说,它需要 1 条后面跟两个表达式的语句),不过它们可以是空表达式(语句),只有两个分号是必需的。另外,省略 for 循环中的测试表达式时,测试结果将为 true,因此下面的循环将一直运行下去:
由于 for 循环和 while 循环几乎是等效的,因此究竟使用哪一个只是风格上的问题。它们之间存在三个差别。首先,在 for 循环中省略了测试条件时,将认为条件为 true;其次,在 for 循环中,可使用初始化语句声明一个局部变量,但在 while 循环中不能这样做;最后,如果循环体中包括 continue 语句,情况将稍有不同,continue 语句将在第 6 章讨论。通常,程序员使用 for 循环来为循环计数,因为 for 循环格式允许将所有相关的信息—初始值、终止值和更新计数器的方法—放在同一个地方。在无法预先知道循环将执行的次数时,程序员常使用 while 循环。
提示:
在设计循环时,请记住下面几条指导原则。
指定循环终止的条件。
在首次测试之前初始化条件。
在条件被再次测试之前更新条件。
for 循环的一个优点是,其结构提供了一个可实现上述 3 条指导原则的地方,因此有助于程序员记住应该这样做。但这些指导原则也适用于 while 循环。
错误的标点符号
for 循环和 while 循环都由用括号括起的表达式和后面的循环体(包含一条语句)组成。前面讲过,这条语句可以是语句块,其中包含多条语句。记住,语句块是由花括号,而不是由缩进定义的。例如,请看下面的循环:
缩进表明,该程序的作者希望 i++;语句是循环体的组成部分。然而,由于没有花括号,因此编译器认为循环体仅由最前面的 cout 语句组成。因此,该循环将不断地打印数组的第一个字符。该程序不会执行 i++;语句,因为它在循环的外面。
下面的例子说明了另一个潜在的缺陷:
这一次,代码正确地使用了花括号,但还插入了一个分号。记住,分号结束语句,因此该分号将结束 while 循环。换句话说,循环体为空语句,也就是说,分号后面没有任何内容。这样,花括号中所有的代码现在位于循环的后面,永远不会被执行。该循环不执行任何操作,是一个死循环。请注意这种分号。
5.2.2 等待一段时间:编写延时循环
有时候,让程序等待一段时间很有用。例如,读者可能遇到过这样的程序,它在屏幕上显示一条消息,而还没来得及阅读之前,又出现了其他内容。这样读者将担心自己错过了重要的、无法恢复的消息。如果程序在显示其他内容之前等待 5 秒钟,情况将会好得多。while 循环可用于这种目的。一种用于个人计算机的早期技术是,让计算机进行计数,以等待一段时间:
这种方法的问题是,当计算机处理器的速度发生变化时,必须修改计数限制。例如,有些为 IBM PC 编写的游戏在速度更快的机器上运行时,其速度将快得无法控制;另外,有些编译器可能修改上述代码,将 wait 设置为 10000,从而跳过该循环。更好的方法是让系统时钟来完成这种工作。
ANSI C 和 C++库中有一个函数有助于完成这样的工作。这个函数名为 clock( ),返回程序开始执行后所用的系统时间。这有两个复杂的问题:首先,clock( ) 返回时间的单位不一定是秒;其次,该函数的返回类型在某些系统上可能是 long,在另一些系统上可能是 unsigned long 或其他类型。
但头文件 ctime(较早的实现中为 time.h)提供了这些问题的解决方案。首先,它定义了一个符号常量—CLOCKS_PER_SEC,该常量等于每秒钟包含的系统时间单位数。因此,将系统时间除以这个值,可以得到秒数。或者将秒数乘以 CLOCK_PER_SEC,可以得到以系统时间单位为单位的时间。其次,ctime 将 clock_t 作为 clock( ) 返回类型的别名(参见本章后面的注释“类型别名”),这意味着可以将变量声明为 clock_t 类型,编译器将把它转换为 long、unsigned int 或适合系统的其他类型。
程序清单 5.14 演示了如何使用 clock( ) 和头文件 ctime 来创建延迟循环。
程序清单 5.14 waiting.cpp
该程序以系统时间单位为单位(而不是以秒为单位)计算延迟时间,避免了在每轮循环中将系统时间转换为秒。
类型别名
C++为类型建立别名的方式有两种。一种是使用预处理器:
这样,预处理器将在编译程序时用 char 替换所有的 BYTE,从而使 BYTE 成为 char 的别名。
第二种方法是使用 C++(和 C)的关键字 typedef 来创建别名。例如,要将 byte 作为 char 的别名,可以这样做:
下面是通用格式:
换句话说,如果要将 aliasName 作为某种类型的别名,可以声明 aliasName,如同将 aliasName 声明为这种类型的变量那样,然后在声明的前面加上关键字 typedef。例如,要让 byte_pointer 成为 char 指针的别名,可将 byte_pointer 声明为 char 指针,然后在前面加上 typedef:
也可以使用#define,不过声明一系列变量时,这种方法不适用。例如,请看下面的代码:
预处理器置换将该声明转换为这样:
typedef 方法不会有这样的问题。它能够处理更复杂的类型别名,这使得与使用#define 相比,使用 typedef 是一种更佳的选择—有时候,这也是唯一的选择。
注意,typedef 不会创建新类型,而只是为已有的类型建立一个新名称。如果将 word 作为 int 的别名,则 cout 将把 word 类型的值视为 int 类型。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论