<p id="main-toc"><strong>目录</strong></p>
讯享网
循环控制结构
当型与直到型循环结构
while 语句
do-while 语句
for 语句
for 循环的执行过程
使用 while 语句等价实现 for 循环
for 循环的不同形式
使用循环计算 1 到 n 的累加和
问题求解方法分析
方法 1: 用 for 语句编程实现
方法 2: 用 while 语句编程实现
方法 3: 用 do-while 语句编程实现
为什么 sum = sum + i 能实现累加功能呢?
逗号运算符
空语句与死循环
while 与 do-while 的区别
章节内容导读:
本章围绕累加求和和累乘求积介绍了计数控制的循环和如何寻找累加或累乘项(通项)的构成规律,围绕猜数游戏介绍了条件控制的循环,围绕韩信点兵实例介绍了穷举法和流程转移控制语句。本章内容对应 “C 语言程序设计精髓” MOOC 课程的第 5 周视频,主要内容如下:
- 计数控制的循环,条件控制的循环,嵌套循环
- for 语句,while 语句,do-while 语句,continue 语句,break 语句
- 结构化程序设计的基本思想,程序调试与排错
第 5 章例 5.5 程序每次运行时只允许进行一次算术运算。能否每次运行程序时允许用户连续进行多次算术运算呢?
若要满足上述用户需求,则要使用本章介绍的循环结构(Loop Structure)。
实际应用中的许多问题,都会涉及重复执行一些操作,如级数求和、穷举或迭代求解等。
- 若需重复处理的次数是已知的,则称为计数控制的循环(Counter Controlled Loop);
- 若重复处理的次数是未知的,是由给定条件控制的,称为条件控制的循环(Condition Controlled Loop)。
二者都需要用循环结构来实现。
按照结构化程序设计的观点,任何复杂问题都可用顺序、选择和循环这三种基本结构编程实现,它们是复杂程序设计的基础。
循环结构通常有两种类型:
- 当型循环结构(如图 6-1 所示),表示当条件 P 成立(为真)时,反复执行 A 操作,直到条件 P 不成立(为假)时结束循环。
- 直到型循环结构(如图 6-2 所示),表示先执行 A 操作,再判断条件 P 是否成立(为真),若条件 P 成立(为真),则反复执行 A 操作,直到条件 P 不成立(为假)时结束循环。
C 语言提供 for 、while 、do-while 三种循环语句(Loop Statement)来实现循环结构。循环语句在给定条件为真的情况下,重复执行一个语句序列,这个被重复执行的语句序列称为循环体(Body of Loop)。
while 语句属于当型循环。其一般形式为:
讯享网
while 语句中的循环控制表达式是在执行循环体之前测试的。其执行过程如下:
- 计算循环控制表达式的值;
- 如果循环控制表达式的值为真,那么就执行循环体中的语句,并返回步骤 1;
- 如果循环控制表达式的值为假,就退出循环,执行循环体后面的语句。
为了使程序易于维护,建议即使循环体内只有一条语句,也将其用花括号括起来。这是因为当需要在循环体中增加语句时,如果忘记加上花括号,那么仅 while 后面的第 1 条语句会被当做循环体中的语句来处理,从而导致逻辑错误。
do-while 语句属于直到型循环。其一般形式为:
与 while 语句不同的是,do-while 语句中的循环控制表达式是在执行循环体之后测试的。do-while 语句的执行过程如下:
- 执行循环体中的语句;
- 计算循环控制表达式的值;
- 如果循环控制表达式的值为真,那么返回步骤 1;
- 如果循环控制表达式的值为假,就退出循环,执行循环体后面的语句。
可见,对于 do-while 语句来说,由于是先执行循环体后计算并判定循环控制条件为真还是为假,所以循环体内的语句将至少被执行一次。
for 语句属于当型循环结构。它的使用方式非常灵活,在 C 语言程序中的使用频率也最高。其一般形式如下:
讯享网
- 初始化表达式:用于为循环控制变量设置起始值,决定循环的起点。
- 循环控制表达式:是判断循环是否继续执行的条件,当该表达式为真时,循环继续;否则,循环结束,决定了循环何时结束。在每次(包括第一次)循环体执行之前,都会检查循环控制表达式。
- 增值表达式:在每次循环体执行完毕后更新循环控制变量的值,决定循环的迭代方式。若在循环体内修改循环控制变量,会改变循环的执行次数。
for 循环的执行过程
for 语句的执行过程如下:
- 初始化:首先执行初始化表达式。这个表达式通常用来设置循环控制变量的初始值。这个步骤只会执行一次,就在整个循环开始之前。如果已经在 for 语句之前初始化了循环控制变量,那么可以在这里省略初始化表达式。
- 条件检测:接着计算循环控制表达式的值。这是一个布尔表达式,用来决定循环是否继续。如果表达式的值为真(非零),则进入循环体执行;如果为假(零),则立即跳出循环,for 语句结束。
- 执行循环体:如果循环控制表达式的值为真,那么执行大括号 { } 内的循环体。循环体可以包含一条或多条语句。
- 更新循环变量:执行完循环体后,执行增值表达式。这一步骤通常用来更新循环控制变量,比如递增或递减。更新之后,控制流程返回到步骤 2,再次评估循环控制表达式。
- 重复步骤 2 至 4:这个过程会一直重复,直到循环控制表达式的值为假。此时,循环终止,程序将继续执行紧跟在 for 循环之后的语句。
使用 while 语句等价实现 for 循环
for 语句可用 while 语句来等价实现,与 for 语句等价的 while 语句的形式为:
for 循环的不同形式
注意:for 语句中三个表达式之间的分隔符是分号,有且仅有两个分号,既不能多,也不能少。
当已在 for 语句前面为循环控制变量赋初值时,初始化表达式可以省略;
讯享网
在这个结构中,初始化表达式在 for 循环之前被声明和初始化,循环控制表达式在每次迭代开始时被评估,如果为真,则执行循环体内的语句序列。增值表达式在每次迭代结束时执行,用于更新循环变量。
当已在循环体中改变了循环控制变量时,增值表达式可以省略。
在这个结构中,初始化表达式同样在 for 循环之前被声明和初始化。循环控制表达式在每次迭代开始时被评估,如果为真,则执行循环体内的语句序列。然而,与第一种结构不同的是,增值表达式被移动到了循环体的末尾,这意味着它将在每次执行完语句序列之后被执行。
一般情况下,循环控制表达式很少省略,若省略,则表示循环条件永真(按照实际需求在循环体里设置循环退出的条件);
讯享网
在这个结构中,for 循环的头部只有两个分号,表示初始化表达式、循环控制表达式、增值表达式均被省略。循环体会一直执行,直到在循环体内遇到 break 语句或其他能够终止循环的语句。通常,我们会在循环体内使用 if 语句来检查某个条件,如果条件满足,则执行 break 语句来退出循环。
需要注意的是,由于这种循环结构很容易创建无限循环,因此在实际编程中应谨慎使用,并确保在循环体内有明确的退出条件。否则,程序可能会陷入死循环,导致系统资源耗尽或程序崩溃。
【例 6.1】 编程从键盘输入 n,然后计算并输出 1 + 2 + 3 + … + n 的值。
将 n 个数相加,用 n 个变量存储 n 个数值再进行相加的方法,显然是不现实的。因为首先不知道 n 的值是多少,因此不知道该定义多少个变量,其次,即使 n 值是确定的,如果 n 值较大,那么需要定义的变量就会很多,而且当 n 值增大时,需要定义的变量数也要增加。
而如果采用循环的方法,每次循环都是在前一次求和的基础上继续累加下一个数,那么循环 n 次就实现了 n 个数相加,此时只需定义三个变量就够了。这种累加求和的方法可用如图 6-3 所示的流程图来描述,也可用自然语言描述如下:
- Step 1 从键盘输入 n 值。
- Step 2 累加求和变量赋初值,sum = 0。
- Step 3 累加次数计数器 i 置初值,i = 1。
- Step 4 若循环次数 i 未超过 n,则反复执行 Step 5 ~ Step 6,否则转去执行 Step 7。
- Step 5 进行累加运算,sum = sum + i。
- Step 6 累加次数计数器 i 加 1,i = i + 1,且转 Step 4。
- Step 7 打印累加结果值 sum。
注意,由于每次循环体执行完以后,都要执行一次增值表达式。因此,这里在最后退出 for 循环后,i 的值实际为 n + 1。
程序的运行结果如下所示:
讯享网
这是因为 sum = sum + i 中赋值运算符右侧的 sum 执行的是读操作,而左侧的 sum 执行的是写操作,即将 sum 的值先读出来,与 i 的值相加后再写回到 sum 中去。
执行第 i 次循环前后,即每次执行语句 sum = sum + i; 前后的 sum 值的变化情况如表 6-1 所示。
从上述分析不难看出,程序第 8 行对累加和变量 sum 初始化为 0 的语句是必不可少的,如果删掉(或注释)这条语句,那么程序在 VS code 下编译后的运行结果如下:
为什么会出现这样的错误结果呢?
以 for 循环实现的程序为例,在循环体中语句 sum = sum + i; 的后面增加如下一条打印语句,观察每一步累加的结果值即可分析出原因所在。
讯享网
在用户输入 n 值为 5 的情况下,我们期望程序输出的运行结果为:
但是由于第 8 行语句被删掉(或注释)了,所以实际的运行结果为:
从这一结果,不难判断错误的原因在于第一次循环之前未将 sum 初始化为 0,未初始化的变量值是一个随机值。因此,最好在第 5 行对变量 sum 定义时对 sum 进行初始化(int sum = 0, i = 1, n;)。养成这种在定义变量的同时为变量进行初始化的习惯,有助于防止此类错误的发生。

在 C 语言中,有一种特殊的运算符称为逗号运算符(Comma Operator)。逗号运算符可把多个表达式连接在一起,构成逗号表达式,其作用是实现对各个表达式的顺序求值,因此逗号运算符也称为顺序求值运算符。其一般形式为:
讯享网
逗号运算符在所有运算符中优先级最低,且具有左结合性。
因此,在执行时,上述表达式的求解过程为:先计算表达式 1 的值,然后依次计算其后的各个表达式的值,最后求出表达式 n 的值,并将最后一个表达式的值作为整个逗号表达式的值。
在许多情况下,使用逗号表达式的目的并非要得到和使用整个逗号表达式的值,更常见的是要分别得到各个表达式的值,主要用在 for 语句中需要同时为多个变量赋初值等情况。例如,表达式 1 可用逗号表达式顺序地执行为多个变量赋初值的操作。同样地,当需要使多个变量的值在每次循环执行后发生变化时,表达式 3 也可使用逗号表达式。
例如,当 n 为偶数时,例 6.1 程序还可用下面方法编程实现。
在这个程序中,第 9 行的 for 语句中有两个循环控制变量,其中表达式 1 和表达式 3 都是一个逗号表达式。
当 n 为 100 时,如图 6-4 所示,循环累加操作是从待累加的等差数列两边开始同时进行的(对应于表达式 1 分别对 i 和 j 初始化为 1 和 n),每次循环累加两个值 i 和 j,其中 i 的值是不断加 1,j 的值是不断减 1(对应于表达式 3 分别对 i 增值 1 和对 j 增值 -1)。这样做的好处是,可以使得循环次数减少为原来的一半(即 50 次)。最后一次执行循环体时,i 值为 50,j 值为 51。当 i 值为 51、j 值为 50 时,因 i <= j 为假而退出循环。
请思考为什么 n 为奇数时按上面方法编程结果有误?
当 n 为奇数时,问题出现在 i 和 j 相遇的那一点。由于 i 和 j 都是从整数开始,并且每次循环都改变它们的值,当 n 是奇数时,i 和 j 会在某个中间值相遇,这个值被加了两次。
例如,如果 n = 5,循环的过程如下:
- 初始时,i = 1, j = 5,sum = 0
- 第一次循环:sum = 0 + 1 + 5 = 6,i = 2, j = 4
- 第二次循环:sum = 6 + 2 + 4 = 12,i = 3, j = 3
- 第三次循环(问题所在):sum = 12 + 3 + 3 = 18,此时 i = 4, j = 2(注意,3 被加了两次)
- 循环结束,因为 i 不再小于等于 j
正确的做法是在循环中避免重复添加相遇的那个值。这可以通过多种方法实现,例如:
- 调整循环条件:确保 i 和 j 不会在循环中相遇,或者当它们相遇时只加一次。
- 使用条件语句:在循环内部检查 i 和 j 是否相等,如果相等则只加一次。
讯享网
或者更简洁地,只循环到 i < j,这样就不需要考虑相等的情况:
这样,无论 n 是奇数还是偶数,程序都能正确计算从 1 到 n 的数字与从 n 到 1 的数字相对应相加的和。
仅由一个分号构成的语句,称为空语句(Null Statement)。
空语句什么也不做,只表示语句的存在。当循环体中是空语句时,表示在循环体中什么也不做,常用于编写延时程序,例如:
讯享网
或者
或者
讯享网
其中,最后一种形式尤其值得注意,除非特殊需要,一般不在 for 语句后加分号。
例如,将例 6.1 程序中的 for 语句写成:
后,它相当于下面的语句序列:
讯享网
将 for 语句末尾加上不该加的分号,是初学者常犯的错误。
如果 for 语句末尾有分号,就表示循环体是分号之前的内容,相当于循环体变成了空语句,表示循环体内什么都不做,将产生逻辑错误。
如果 while 后面被意外地加上分号,那么情况会更糟,有可能产生死循环(Endless Loop)。例如:
它相当于下面的语句序列:
讯享网
由于在 while 语句之前对循环控制变量 i 初始化为 1,如果用户输入大于 1 的 n 值,那么因循环体中没有语句改变循环控制变量 i 的值,使得 while 后括号内的循环条件永真,从而使该循环成为死循环。
在例 6.1 中,用 for 语句、while 语句和 do-while 语句分别编写了程序,虽然在这里看上去它们的作用是等价的,但其实它们并非在任何时候都是等价的。
当第一次测试循环条件就为假时,while 语句和 do-while 语句是不等价的。例如,下面两段程序就不是等价的。
这个程序因为是先判断后执行,所以当 n 初值不满足 while 语句的循环条件时,循环一次也不执行,因此什么都没有打印。
讯享网
这个程序虽然 n 初值不满足 while 语句的循环条件,但因为是先执行后判断,即已经执行了一次循环后才进行判断,所以循环至少被执行一次,因此,打印结果为 n = 101。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/140534.html