第2单元控制结构 第2单元控制结构 本单元教学目标 介绍结构化程序设计方法的基本思想以及C++的基本控制结构和控制转移语句 学习要求 通过本单元学习,掌握结构化程序设计方法的基本思想和C++的几种基本控制转移语 句,熟悉使用伪代码的编程方法 授课内容 21程序的基本控制结构 我们知道,C++源程序由若干函数构成,而函数又是由语句构成的。对于一个程序员来 说,编程序的一个主要内容就是如何将解决一个应用问题所使用的算法用C++的语句和函 数来描述。换句话说,也就是如何组织C++程序的结构。在本单元中,首先要介绍的是构 成程序的几种基本结构,并进而介绍如何使用这些基本结构编写比较复杂的程序。 结构化设计方法是以模块化设计为中心,将待开发的软件系统划分为若干个相互独立的 模块,这样使完成每一个模块的工作变单纯而明确,为设计一些较大的软件打下了良好的基 础。由于模块相互独立,因此在设计其中一个模块时,不会受到其它模块的牵连,因而可将 原来较为复杂的问题化简为一系列简单模块的设计。模块的独立性还为扩充已有的系统、建 立新系统带来了不少的方便,因为我们可以充分利用现有的模块作积木式的扩展。按照结构 化设计方法设计出的程序具有结构清晰、可读性好、易于修改和容易验证的优点。C++是 种支持结构化程序设计思想的程序设计语言,使用C++编写程序时,应该遵循结构化程 序设计方法 在结构化程序设计方法中,模块是一个基本概念。一个模 块可以是一条语句、一段程序、一个函数等。在流程图中,模 程序模块 块用一个矩形框表示,如图2-1所示。模块的基本特征是其仅 有一个入口和一个出口,即要执行该模块的功能,只能从该模 块的入口处开始执行(用图2-1矩形上面的有向线段表示)执 行完该模块的功能后,从模块的出口转而执行其他模块的功图2-1程序模块 能(用图2-1矩形下面的有向线段表示),即使模块中包含多个
第 2 单元 控制结构 - 17 - 第 2 单元 控制结构 本单元教学目标 介绍结构化程序设计方法的基本思想以及C++的基本控制结构和控制转移语句。 学习要求 通过本单元学习,掌握结构化程序设计方法的基本思想和C++的几种基本控制转移语 句,熟悉使用伪代码的编程方法。 授课内容 2.1 程序的基本控制结构 我们知道,C++源程序由若干函数构成, 而函数又是由语句构成的。对于一个程序员来 说,编程序的一个主要内容就是如何将解决一个应用问题所使用的算法用C++的语句和函 数来描述。换句话说,也就是如何组织C++程序的结构。在本单元中,首先要介绍的是构 成程序的几种基本结构,并进而介绍如何使用这些基本结构编写比较复杂的程序。 结构化设计方法是以模块化设计为中心,将待开发的软件系统划分为若干个相互独立的 模块, 这样使完成每一个模块的工作变单纯而明确,为设计一些较大的软件打下了良好的基 础。由于模块相互独立,因此在设计其中一个模块时,不会受到其它模块的牵连,因而可将 原来较为复杂的问题化简为一系列简单模块的设计。模块的独立性还为扩充已有的系统、建 立新系统带来了不少的方便,因为我们可以充分利用现有的模块作积木式的扩展。按照结构 化设计方法设计出的程序具有结构清晰、可读性好、易于修改和容易验证的优点。C++是 一种支持结构化程序设计思想的程序设计语言, 使用C++编写程序时, 应该遵循结构化程 序设计方法。 在结构化程序设计方法中, 模块是一个基本概念。一个模 块可以是一条语句、一段程序、一个函数等。在流程图中, 模 块用一个矩形框表示, 如图 2-1 所示。模块的基本特征是其仅 有一个入口和一个出口, 即要执行该模块的功能, 只能从该模 块的入口处开始执行 (用图2-1矩形上面的有向线段表示), 执 行完该模块的功能后, 从模块的出口转而执行其他模块的功 能 (用图2-1矩形下面的有向线段表示), 即使模块中包含多个 程序模块 图2-1 程序模块
第2单元控制结构 语句,也不能随意从其他语句开始执行,或提前退出模块 按照结构化程序设计的观点,任何算法功能都可以通过由程序模块组成的三种基本程 序结构的组合:顺序结构、选择构和循环结构来实现。 顺序结构由两个程序模块串接构成,如图2-2左部所示。由图中可以看出,这两个程序 模块是顺序执行的,即首先执行,然后执行。 顺序结构中的两个程 序模块可以合并成一个新 的程序模块,即将图2-2 程序模块1 中的左边部分整个看成一1 个新的模块,如图2-2的1 新程序模块 右部。通过这种方法,可 程序模块2 以将许多顺序执行的语句 合并成一个比较大的程序 模块。但无论怎样合并, 生成的新的程序模块仍然 是一个整体,只能从模块 图2-2顺序结构 的顶部(入口)进入模块 开始执行模块中的语句,执行完模块中的所有语句之后再从模块的底部(出口)退出模块。 事实上,顺序结构是最常见的程序结构形式,在一般程序中大量存在。但是设想一下,是不 是所有程序都可以只使用顺序结构编写呢?显然答案是否定的。在求解实际问题时,常常要 根据输入数据的实际情况进行逻辑判断,对不同的结果分别进行不同的处理:或者需要反复 执行某些程序段落,以避免多次重复编写结构相似的程序段落带来的程序结构上的臃肿。这 就需要在程序中引入选择结构和循环结构。一个结构化程序正是由这三种基本程序结构交替 综合而构成的。 选择结构如图2-3所 示。从图中可以看出,根 据逻辑条件成立与否,分 不成立 条件 别选择执行或 者。虽然选择结 成立 新程序模块 构比顺序结构稍微复杂了 程序模块1 程序模块2 点,但是仍然可以将其 整个作为一个新的程序模 块:一个入口(从顶部进 入模块开始判断),一个出 图2-3选择结构(多分支) 口(无论执行了还 是,都应从选择结构框的底部出去)。 在编程实践中,还可能遇到选择结构中的一个分支没有实际操作的情况,如图2-4所示。 这种形式的选择结构可以看成是图2-3中的选择结构的特例
第 2 单元 控制结构 - 18 - 语句, 也不能随意从其他语句开始执行, 或提前退出模块。 按照结构化程序设计的观点, 任何算法功能都可以通过由程序模块组成的三种基本程 序结构的组合: 顺序结构、选择构和循环结构来实现。 顺序结构由两个程序模块串接构成, 如图 2-2 左部所示。由图中可以看出, 这两个程序 模块是顺序执行的, 即首先执行, 然后执行。 顺序结构中的两个程 序模块可以合并成一个新 的程序模块,即将图 2-2 中的左边部分整个看成一 个新的模块,如图 2-2 的 右部。通过这种方法,可 以将许多顺序执行的语句 合并成一个比较大的程序 模块。但无论怎样合并, 生成的新的程序模块仍然 是一个整体,只能从模块 的顶部 (入口) 进入模块 开始执行模块中的语句,执行完模块中的所有语句之后再从模块的底部 (出口) 退出模块。 事实上,顺序结构是最常见的程序结构形式,在一般程序中大量存在。但是设想一下,是不 是所有程序都可以只使用顺序结构编写呢?显然答案是否定的。在求解实际问题时,常常要 根据输入数据的实际情况进行逻辑判断,对不同的结果分别进行不同的处理;或者需要反复 执行某些程序段落,以避免多次重复编写结构相似的程序段落带来的程序结构上的臃肿。这 就需要在程序中引入选择结构和循环结构。一个结构化程序正是由这三种基本程序结构交替 综合而构成的。 选择结构如图 2-3 所 示。从图中可以看出,根 据逻辑条件成立与否,分 别选择执行 或 者。虽然选择结 构比顺序结构稍微复杂了 一点,但是仍然可以将其 整个作为一个新的程序模 块:一个入口 (从顶部进 入模块开始判断),一个出 口(无论执行了还 是,都应从选择结构框的底部出去)。 在编程实践中,还可能遇到选择结构中的一个分支没有实际操作的情况,如图 2-4 所示。 这种形式的选择结构可以看成是图 2-3 中的选择结构的特例。 程序模块1 图2-2 顺序结构 程序模块2 新程序模块 图2-3 选择结构(多分支) 程序模块1 新程序模块 条件 程序模块2 不成立 成立
循环结构如图2-5所 示。在进入循环结构后首先---- 判断条件是否成立,如果成 不成立1 立则执行,反之 条件 则退出循环结构。执行完后再去判断条件 如果条件仍然成立则再次执1程序模块 行内嵌的,循环 往复,直至条件不成立时退L_ 出循环结构。 与顺序和选择结构相 图2-4一个分支没有实际操作的选择结构 同,循环结构也可以抽象为 个新的模块。图2-5中的循 环结构可以描述为“当条件成 立时反复执行程序模块”,故 不成立 又称为 while(当)型循环。除 成立 程序模块 了 while型循环以外,还可以 构造出其他类型的循环来,如 程序模块 do- while型循环结构,其特点 是进入循环结构后首先执行,然后再判断条件 是否成立,如果成立则再次执 图2.5 while循环结构 行,直到条件不成 立时退出循环结构。do- while型循环结构如图2-6所示。 程序模块 新程序模块 条件 不成立 图2-6do- while型循环
第 2 单元 控制结构 - 19 - 循环结构如图 2-5 所 示。在进入循环结构后首先 判断条件是否成立,如果成 立则执行,反之 则退出循环结构。执行完后再去判断条件, 如果条件仍然成立则再次执 行内嵌的,循环 往复,直至条件不成立时退 出循环结构。 与顺序和选择结构相 同, 循环结构也可以抽象为 一个新的模块。图 2-5 中的循 环结构可以描述为“当条件成 立时反复执行程序模块”,故 又称为 while (当) 型循环。除 了 while 型循环以外, 还可以 构造出其他类型的循环来,如 do-while 型循环结构,其特点 是进入循环结构后首先执行,然后再判断条件 是否成立,如果成立则再次执 行,直到条件不成 立时退出循环结构。do-while 型循环结构如图 2-6 所示。 图2-4 一个分支没有实际操作的选择结构 程序模块 新程序模块 条件 不成立 成立 条件 成立 不成立 图2-6 do-while型循环 新程序模块 程序模块 图2.5 while循环结构 条件 成立 不成立 新程序模块 程序模块
22“自顶向下,逐步求精”的程序设计方法 在上面介绍的顺序、选择和循环三种基本程序结构中的程序模块又可以是由这三种基本 程序结构抽象而成的新程序模块,因此可以通过组合、嵌套这些基本程序结构来构造更复杂 的程序。已经证明,一个合理的程序,总可以化为顺序、选择和循环这三种基本程序结构的 某种组合。 结构化程序设计支持“自顶向下,逐步求精”的程序设计方法。自顶向下的程序设计方 法从问题本身开始,经过逐步求精,将解决问题的步骤分解为由基本程序结构模块组成的结 构化程序框图,据此就很容易编写出结构良好、易于调试的程序来。 「例2-1“验证”哥德巴赫猜想。哥德巴赫猜想是数论中的一个著名难题,是由法国数学 爱好者克里斯蒂安·德巴赫于1742年在给著名数学家欧拉的一封信中提出的。“哥德巴赫 猜想”可以表述为:任何一个大于等于4的偶数均可以表示为两个素数之和。尽管这个问题 看来如此简明清晰,但二百多年来虽有无数数学家为其呕心沥血、绞尽脑汁,却始终无人 能够证明或者证伪这个猜想2。 我们将这个问题作为一个练习,在有限的范围内验证哥德巴赫猜想:编写一段程序,验 证大于4,小于某一上限M的所有偶数是否都能被分解为两个素数之和。如果一但发现某个 偶数不能被分解为两个素数之和,则证实了哥德巴赫猜想是错误的(果真如此,则可称是数 学史上的一大发现!):否则证实哥德巴赫猜想在所给的范围内成立 算法:首先画出代表解决该问题的程序模块,见图2-7 然后根据题意对图27中的程序模块进行初步 分解,其思路如下:逐个生成由4到M之间的所有 偶数,一一验证其是否能够被分解为两个素数之和。 验证哥德巴赫猜想 具体方法是声明一个变量x,令其初值等于4,然后 每次在x上加2,以产生各偶数并验证x是否可以被 分解为两个素数之和直到x不小于M为止。显然,图2-7验证哥德巴赫猜想 这是一个循环结构,其分解图见图2-8。 在图2-8中的初步分解中用粗线框表示了基本程序结构的嵌套组合情况。从图中可以 看出,最内层的粗线框中是两个顺序排列的程序模块,它们一起构成了循环结构的内嵌程序 模块。循环模块又和最前面的语句模块构成了顺序结构 1如果一个程序满足下列条件,就是一个合理的程序 1.整个程序只有一个入口和一个出口 2.从程序的入口到所有模块都至少有一条路径可达。 这些条件是非常容易满足的。通过合并那些不止一个的入口和出口,以及删除那些不可到达的模块(这些模 块对程序的功能没有任何影响,任何程序都可以化为一个合理的程序。 260-70年代,我国数学家陈景润证明了一个条件略宽的定理,即“任何一个大于4的偶数,均可以分解为 一个素数与两个素数的积之和”,从而向最终解决哥德巴赫猜想又迈进了一步
第 2 单元 控制结构 - 20 - 2.2 “自顶向下, 逐步求精”的程序设计方法 在上面介绍的顺序、选择和循环三种基本程序结构中的程序模块又可以是由这三种基本 程序结构抽象而成的新程序模块, 因此可以通过组合、嵌套这些基本程序结构来构造更复杂 的程序。已经证明, 一个合理的程序1 , 总可以化为顺序、选择和循环这三种基本程序结构的 某种组合。 结构化程序设计支持“自顶向下, 逐步求精”的程序设计方法。自顶向下的程序设计方 法从问题本身开始,经过逐步求精,将解决问题的步骤分解为由基本程序结构模块组成的结 构化程序框图,据此就很容易编写出结构良好、易于调试的程序来。 [例 2-1]“验证”哥德巴赫猜想。哥德巴赫猜想是数论中的一个著名难题, 是由法国数学 爱好者克里斯蒂安·哥德巴赫于 1742 年在给著名数学家欧拉的一封信中提出的。“哥德巴赫 猜想”可以表述为:任何一个大于等于 4 的偶数均可以表示为两个素数之和。尽管这个问题 看来如此简明清晰, 但二百多年来, 虽有无数数学家为其呕心沥血、绞尽脑汁, 却始终无人 能够证明或者证伪这个猜想2。 我们将这个问题作为一个练习, 在有限的范围内验证哥德巴赫猜想:编写一段程序, 验 证大于 4, 小于某一上限 M 的所有偶数是否都能被分解为两个素数之和。如果一但发现某个 偶数不能被分解为两个素数之和, 则证实了哥德巴赫猜想是错误的 (果真如此, 则可称是数 学史上的一大发现!);否则证实哥德巴赫猜想在所给的范围内成立。 算 法: 首先画出代表解决该问题的程序模块, 见图 2-7。 然后根据题意对图 2-7 中的程序模块进行初步 分解, 其思路如下:逐个生成由 4 到 M 之间的所有 偶数,一一验证其是否能够被分解为两个素数之和。 具体方法是声明一个变量 x, 令其初值等于 4, 然后 每次在 x 上加 2, 以产生各偶数并验证 x 是否可以被 分解为两个素数之和, 直到 x 不小于 M 为止。显然, 这是一个循环结构, 其分解图见图 2-8。 在图 2-8 中的初步分解中用粗线框表示了基本程序结构的嵌套组合情况。 从图中可以 看出, 最内层的粗线框中是两个顺序排列的程序模块, 它们一起构成了循环结构的内嵌程序 模块。循环模块又和最前面的语句模块构成了顺序结构。 1 如果一个程序满足下列条件, 就是一个合理的程序: 1. 整个程序只有一个入口和一个出口; 2. 从程序的入口到所有模块都至少有一条路径可达。 这些条件是非常容易满足的。通过合并那些不止一个的入口和出口, 以及删除那些不可到达的模块(这些模 块对程序的功能没有任何影响), 任何程序都可以化为一个合理的程序。 2 60-70 年代, 我国数学家陈景润证明了一个条件略宽的定理, 即“任何一个大于 4 的偶数, 均可以分解为 一个素数与两个素数的积之和”, 从而向最终解决哥德巴赫猜想又迈进了一步。 验证哥德巴赫猜想 图2-7验证哥德巴赫猜想
图2-8中的框图是相当粗糙的,因为如何“验证x是否能被分解为两个素数之和”并不 清楚,因此继续对这个问题进行分解。“验证x是否能被分解为两个素数之和”的步骤可以 这样考虑:首先用x减去最小的素数2,然后看其差是否仍为素数,如果是,则验证结束,可 以打印出该偶数的分解表达式。否则,换一个更大的素数,再看x与这个素数的差是否为素 数。如果不是则仍进行循环,直到用于检测的素数已经大于x2而x与其差仍不是素数。这 时即可宣布一个伟大的发现:哥德巴赫猜想不成立!(?!) 图2-9给出了图2-8中的程序模块“验 证x是否能被分解为两个素数之和”的进 步分解。这里引入了一个新的变量p,用 于存放已经生成的素数。 图2-9中的模块“生成下一个素数” 还可以继续分解。实际上,在大多数程序 否 设计语言中,条件“x-p不是素数?”并不 能简单地写成一个表达式,也需要进一步 细化分解。在实际编程时,既可以将图2-9 直接代入图2-8,编成一个较大的程序,也 验证x是否能被分 解为两个素数之和 可以单独将图2-9中的程序模块编写为一 个函数,由主函数调用。一般来讲,一个程 序模块的大小最好不要超过一页打印纸1 +2 (约60行左右),这样程序的结构比较清 晰。如果程序模块太大则可以进一步分解 以上过程可以总结如下: 首先从题目本身开始,找出解决问题:L 的基本思路,并将其用结构化框图表示出 来。这个框图可能是非常粗糙的,仅仅是 一个算法的轮廓,但可以作为进一步分析图2-8验证哥德巴赫猜想的初步分解 x/2且xD不是常费、否 验证x是否能分解为 两个素数之和 生成下一个素数 不成立的情况 打印出X的分解情况 图2-9“验证x是否能分解为两个素数之和”的进一步分解
第 2 单元 控制结构 - 21 - 图 2-8 中的框图是相当粗糙的,因为如何“验证 x 是否能被分解为两个素数之和”并不 清楚, 因此继续对这个问题进行分解。“验证 x 是否能被分解为两个素数之和”的步骤可以 这样考虑: 首先用 x 减去最小的素数 2, 然后看其差是否仍为素数, 如果是, 则验证结束, 可 以打印出该偶数的分解表达式。否则, 换一个更大的素数, 再看 x 与这个素数的差是否为素 数。如果不是则仍进行循环, 直到用于检测的素数已经大于 x/2 而 x 与其差仍不是素数。这 时即可宣布一个伟大的发现: 哥德巴赫猜想不成立!(?!) 图 2-9 给出了图 2-8 中的程序模块“验 证 x 是否能被分解为两个素数之和”的进 一步分解。这里引入了一个新的变量 p, 用 于存放已经生成的素数。 图 2-9 中的模块“生成下一个素数” 还可以继续分解。实际上, 在大多数程序 设计语言中,条件“x–p 不是素数?”并不 能简单地写成一个表达式, 也需要进一步 细化分解。在实际编程时, 既可以将图 2-9 直接代入图2-8, 编成一个较大的程序, 也 可以单独将图 2-9 中的程序模块编写为一 个函数, 由主函数调用。一般来讲, 一个程 序模块的大小最好不要超过一页打印纸 (约 60 行左右),这样程序的结构比较清 晰。如果程序模块太大则可以进一步分解。 以上过程可以总结如下: 首先从题目本身开始, 找出解决问题 的基本思路, 并将其用结构化框图表示出 来。这个框图可能是非常粗糙的, 仅仅是 一个算法的轮廓, 但可以作为进一步分析 x = 4 x =x/2? 处理哥德巴赫猜想 不成立的情况 打印出X的分解情况 否 是 否 图2-9 “验证x是否能分解为两个素数之和”的进一步分解
的基础。接下来就应该对框图中的那些比较抽象的、用文字描述的程序模块作进一步的分析 细化,每次细化的结果仍用结构化框图表示。最后,对如何求解问题的所有细节都弄清楚了, 就可以根据这些框图直接写出相应的程序代码。这就是所谓的“自顶向下,逐步求精”的程 序设计方法。在分析的过程中用结构化框图表示解题思路的优点是框图中的每个程序模块与 其他程序模块之间的关系非常简明,每次可以只集中精力分解其中的一个模块而几乎不影 响整个程序的结构 23C++的控制结构 23.1顺序结构 在用C艹+编写程序时,实现顺序结构的方法非常简单:只需将两个语句顺序排列即可。 如例1-1中交换两个整数的值的程序段 就是顺序结构 232选择结构 C++的选择结构是通过if-else语句实现的。其格式为 if() ; 将其与图2-3比较,就会发现“程序模块1”和“程序模块2”分别对应于 if-else语句中的 内嵌语句1”和“内嵌语句2”。一般来说,内嵌语句可以是各种可以执行的语句,甚至包 括if-else语句和后面要介绍的循环语句。但是如果“程序模块1”和“程序模块2”比较复 杂,不能简单地用一条语句实现时怎么办呢?这时可以使用由一对花括号“{}”括起来的程 序段落代替“语句1”和“语句2”,即 if() else 这种用花括号括起来的程序段落又称为分程序。分程序是C++中的一个重要概念。具
第 2 单元 控制结构 - 22 - 的基础。接下来就应该对框图中的那些比较抽象的、用文字描述的程序模块作进一步的分析 细化, 每次细化的结果仍用结构化框图表示。最后, 对如何求解问题的所有细节都弄清楚了, 就可以根据这些框图直接写出相应的程序代码。这就是所谓的“自顶向下, 逐步求精”的程 序设计方法。在分析的过程中用结构化框图表示解题思路的优点是框图中的每个程序模块与 其他程序模块之间的关系非常简明, 每次可以只集中精力分解其中的一个模块而几乎不影 响整个程序的结构。 2.3 C++的控制结构 2.3.1 顺序结构 在用C++编写程序时, 实现顺序结构的方法非常简单: 只需将两个语句顺序排列即可。 如例 1-1 中交换两个整数的值的程序段: r = p; p = q; q = r; 就是顺序结构。 2.3.2 选择结构 C++的选择结构是通过 if-else 语句实现的。其格式为: if() ; else ; 将其与图 2-3 比较, 就会发现“程序模块 1”和“程序模块 2”分别对应于 if-else 语句中的 “内嵌语句 1”和“内嵌语句 2”。一般来说, 内嵌语句可以是各种可以执行的语句, 甚至包 括 if-else 语句和后面要介绍的循环语句。但是如果“程序模块 1”和“程序模块 2”比较复 杂, 不能简单地用一条语句实现时怎么办呢? 这时可以使用由一对花括号“{}”括起来的程 序段落代替“语句 1”和“语句 2”, 即: if() { …... } else { …... } 这种用花括号括起来的程序段落又称为分程序。分程序是C++中的一个重要概念。具
第2单元控制结构 体说来,一个分程序具有下述形式 局部数据声明部分〉 执行语句段落〉 即分程序是由花括号括起来的一组语句。当然,分程序中也可以再嵌套新的分程序。分程序 是C++程序的基本单位之 分程序在语法上是一个整体,相当于一个语句。因此分程序可以直接和各种控制语句直 接结合使用,用以构成C++程序的各种复杂的控制结构。在分程序中声明的变量的作用范围 仅限于该分程序内部 在讧语句中用的值来判断程序的流向,如果的值不为0,表示条件成 此时执行;否则(即的值等于0)执行。作条件用 的表达式中通常含有比较运算符或逻辑运算符,例如 /x大于y则表达式的值非0,否则表达式的值为0 x>=0.0&&x) ) 即如果的值不为0时执行或分程序,否则直接执行if语句后面的其他 语句 233循环结构 hile型循环结构可以使用 while语句实现 while() 循环体〉 其中的可以是一个语句,也可以是一个分程序 while()
第 2 单元 控制结构 - 23 - 体说来, 一个分程序具有下述形式 { } 即分程序是由花括号括起来的一组语句。当然, 分程序中也可以再嵌套新的分程序。分程序 是C++程序的基本单位之一。 分程序在语法上是一个整体, 相当于一个语句。因此分程序可以直接和各种控制语句直 接结合使用, 用以构成C++程序的各种复杂的控制结构。在分程序中声明的变量的作用范围 仅限于该分程序内部。 在 if 语句中用的值来判断程序的流向, 如果的值不为 0, 表示条件成 立, 此时执行; 否则 ( 即的值等于 0 ) 执行。作条件用 的表达式中通常含有比较运算符或逻辑运算符, 例如 x>y // x 大于 y 则表达式的值非 0, 否则表达式的值为 0 x>=0.0 && x) ; 或者 if () { …... } 即如果的值不为 0 时执行或分程序, 否则直接执行 if 语句后面的其他 语句。 2.3.3 循环结构 while 型循环结构可以使用 while 语句实现: while () 其中的可以是一个语句, 也可以是一个分程序: while()
第2单元控制结构 whle语句的执行过程见图2-5。当表达式的结果不为0时反复执行其循环体内的语句 或者分程序,直到表达式的值为0时退出循环。在设计 while型循环时要注意在其循环体内 应该有修改的部分,确保在执行了一定次数之后可以退出循环,否则就成了“死循 环”,一旦程序进入这里,将永远在循环结构中兜圈子而无法结束。 do- while型循环结构可以使用do- while语句实现: ); do- while型循环和 while型循环最大的不同是 执行表达式1 do- while循环的循环体最 少执行一次,而 while型循 环的循环体可能一次也不执 行。这一点可以很容易地从 新程序模块 图25和图26的比较中看 执行表达式3 除此而外,C++中还有 种for语句,用来实现一 类比较复杂的循环结构。其 图2-10for循环结构 格式为 for(;;<表达式3》) <循环体〉 其控制流程见图2-10 和 while语句的情况类似,for语句的循环体也可以是一条语句,或者一个分程序。for语 句最常见的用途是构造指定重复次数的循环结构。例如 for(i=0: i<10 1) 用于实现重复10次的循环。虽然用 while循环也可以构造出这样的循环,但使用for语句更 简单、直观。特别是在处理数组时,大多数程序员都喜欢使用for语句
第 2 单元 控制结构 - 24 - { …... } while 语句的执行过程见图 2-5。当表达式的结果不为 0 时反复执行其循环体内的语句 或者分程序, 直到表达式的值为 0 时退出循环。在设计 while 型循环时要注意在其循环体内 应该有修改的部分, 确保在执行了一定次数之后可以退出循环, 否则就成了“死循 环”, 一旦程序进入这里, 将永远在循环结构中兜圈子而无法结束。 do-while 型循环结构可以使用 do-while 语句实现: do { }while (); do-while 型循环和 while 型循环最大的不同是 do-while 循环的循环体最 少执行一次, 而 while 型循 环的循环体可能一次也不执 行。这一点可以很容易地从 图 2-5 和图 2-6 的比较中看 出。 除此而外, C++中还有 一种 for 语句, 用来实现一 类比较复杂的循环结构。其 格式为: for (; ; ) 其控制流程见图 2-10。 和 while 语句的情况类似, for 语句的循环体也可以是一条语句, 或者一个分程序。for 语 句最常见的用途是构造指定重复次数的循环结构。例如 for (i=0; i0? 执行表达式3 否 是 新程序模块 图2-10 for循环结构
第2单元控制结构 24伪代码 前面介绍了利用流程图实现自顶向下的程序设计方法。实际上,在使用C++的程序员们 中还流行着一种利用伪代码进行自顶向下程序设计的方法。伪代码使用C++的几种控制语 句和自然语言结合起来描述算法,比画流程图省时省力,且更容易转化为程序。 例2-2编出“验证”哥德巴赫猜想的程序 算法:对图29中的各程序模块作进一步的分解。这一次采用伪代码表示程序求精 分解的结果。“验证”哥德巴赫猜想首先要解决如何生成和测试素数的问题。素数即质数,即 除了1和自身之外没有其他因子的正整数。大多数素数都不能简单地根据某个数学表达式计 算出来,只能通过逐个检验正整数是否符合素数的定义的方法得出。因此,可以先编写一个 生成素数表的函数,用其生成给定范围内所有的素数并存放在一个素数表中,这样“求一个 素数”或检查一个数是否素数的工作就可以转化为对素数表的查询了 我们使用“筛法”来生成素数表,这是生成素数最古老,同时也是最有效的方法,由古 希腊的哲学家兼数学家埃拉托色尼提出。因为准备验证的最大偶数小于M,所以用到的最大 素数也不会超过M。因此,列出0到M之间的所有自然数 0,1,2,3,4,5,6,7,8,…,M- 然后将所有2的倍数,3的倍数,5的倍数,…,直到M2的倍数均从表中删去(置换为0) 即可。 我们用数组 Primelist存放生成的素数。按上法生成的素数表有一个特点,即如果 Primelist[x]≠0,则x为素数,否则x为合数这样,就可以很方便地根据该素数生成素数,或 者判别一个数是否素数了。我们将生成素数表的工作编成一个函数,其原型为 void CreatePrimeList(int PrimeList[) 用伪代码描述生成素数表的算法如下 将 PrimeList的各元素设置为2到M-1之间的自然数 分别从表中去掉已经确定的各素数的倍数(将其置换为0); 第1步可以用一个for型的循环实现 for(i=0: i<M: i=i+1) Primelist[i] =i 第2步稍微复杂些。我们使用工作变量i指示出最后确定的素数的位置,在表中将数组 元素 Prelist[的倍数转换为0: while(i<M/2) for (j=i+l: j<M; j=j+1) if( PrimeList [j]≠0且是 Primelist[i]的倍数)
第 2 单元 控制结构 - 25 - 2.4 伪代码 前面介绍了利用流程图实现自顶向下的程序设计方法。实际上, 在使用C++的程序员们 中还流行着一种利用伪代码进行自顶向下程序设计的方法。伪代码使用C++的几种控制语 句和自然语言结合起来描述算法, 比画流程图省时省力, 且更容易转化为程序。 [例 2-2] 编出“验证”哥德巴赫猜想的程序。 算 法:对图 2-9 中的各程序模块作进一步的分解。这一次采用伪代码表示程序求精 分解的结果。“验证”哥德巴赫猜想首先要解决如何生成和测试素数的问题。素数即质数, 即 除了 1 和自身之外没有其他因子的正整数。大多数素数都不能简单地根据某个数学表达式计 算出来, 只能通过逐个检验正整数是否符合素数的定义的方法得出。因此,可以先编写一个 生成素数表的函数, 用其生成给定范围内所有的素数并存放在一个素数表中, 这样“求一个 素数”或检查一个数是否素数的工作就可以转化为对素数表的查询了。 我们使用“筛法”来生成素数表, 这是生成素数最古老, 同时也是最有效的方法, 由古 希腊的哲学家兼数学家埃拉托色尼提出。因为准备验证的最大偶数小于 M, 所以用到的最大 素数也不会超过 M。因此, 列出 0 到 M 之间的所有自然数: 0, 1, 2, 3, 4, 5, 6, 7, 8, ..., M−1 然后将所有 2 的倍数, 3 的倍数, 5 的倍数, ..., 直到 M/2 的倍数均从表中删去 ( 置换为 0 ) 即可。 我们用数组 PrimeList 存放生成的素数。按上法生成的素数表有一个特点, 即如果 PrimeList[x]≠0, 则 x 为素数, 否则 x 为合数。这样,就可以很方便地根据该素数生成素数, 或 者判别一个数是否素数了。我们将生成素数表的工作编成一个函数, 其原型为: void CreatePrimeList(int PrimeList[]); 用伪代码描述生成素数表的算法如下: 将 PrimeList 的各元素设置为 2 到 M-1 之间的自然数; 分别从表中去掉已经确定的各素数的倍数 ( 将其置换为 0 ) ; 第 1 步可以用一个 for 型的循环实现: for(i=0; i<M; i=i+1) PrimeList[i] = i; 第 2 步稍微复杂些。我们使用工作变量 i 指示出最后确定的素数的位置, 在表中将数组 元素 PrimeList[i]的倍数转换为 0: i = 2; while(i<M/2) { for(j=i+1; j<M; j=j+1) if(PrimeList[j]≠0 且是 PrimeList[i]的倍数)
i=下一个确定的素数的位置; 这里要注意的是,由于在转换的过程中表中会产生一些值为0的元素,所以在计算下一个确 定的素数的位置时要跳过这些0 while(PrimeList[i]==0) 将图2-9中的程序模块“生成下一个素数”也编为一个函数: int NextPrime Number (int p, int PrimeList[) 其参数为当前的素数p,返回值为素数表 Primelist中的下一个素数。求下一个素数的方法很 简单:从 Primelist[p+1开始向后找到的第一个非0元素,即为所要求的素数 综合以上分析结果和例2-1中的结构化程序框图,就可以编写程序了 程序 ∥/ Example2-2:"验证"哥德巴赫猜想 #include #define m 10001 /定义验证范围 /函数 Creat Primelist0:生成素数表 void CreatPrimeList(int PrimeListO) /将 Primelist的各元素设置为从2开始的正整数 for (i=0: i<M: i i+1) PrimeList[i=i //分别从表中去掉已经确定的各素数的倍数(将其置为 le(i<M/2) for (j=i+l: j<M: j=j+1) if(PrimeList[j]=0 & PrimeList [j]%PrimeList[i]==O) PrimeList [j] =0 ∥/确定下一个素数的位置
第 2 单元 控制结构 - 26 - PrimeList[j] = 0; i = 下一个确定的素数的位置; } 这里要注意的是, 由于在转换的过程中表中会产生一些值为 0 的元素,所以在计算下一个确 定的素数的位置时要跳过这些 0: i = i+1; while(PrimeList[i]==0) i = i+1; 将图 2-9 中的程序模块“生成下一个素数”也编为一个函数: int NextPrimeNumber(int p, int PrimeList[]) { … ... } 其参数为当前的素数 p, 返回值为素数表 PrimeList 中的下一个素数。求下一个素数的方法很 简单: 从 PrimeList[p+1]开始向后找到的第一个非 0 元素, 即为所要求的素数。 综合以上分析结果和例 2-1 中的结构化程序框图, 就可以编写程序了: 程 序: // Example 2-2:"验证"哥德巴赫猜想 #include #define M 10001 // 定义验证范围 // 函数 CreatPrimeList(): 生成素数表 void CreatPrimeList(int PrimeList[]) { int i, j; // 将 PrimeList 的各元素设置为从 2 开始的正整数 for(i=0; i<M; i = i+1) PrimeList[i] = i; // 分别从表中去掉已经确定的各素数的倍数(将其置为 0) i = 2; while(i<M/2) { for(j=i+1; j<M; j=j+1) if(PrimeList[j]!=0 && PrimeList[j]%PrimeList[i]==0) PrimeList[j] = 0; // 确定下一个素数的位置