返回介绍

5.2 while 循环

发布于 2024-10-08 23:14:03 字数 6810 浏览 0 评论 0 收藏 0

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文