第 16 讲:程序结构(二):循环结构
如果我们重复执行特别多相同或相似的操作,如果使用一句一句代码来书写的话,显然是会使得程序臃肿的,而且还麻烦。于是,循环结构就诞生了。
C# 里提供了四种循环结构语句:while
语句、do
-while
语句、for
循环和 foreach
循环。下面我们挨个进行介绍。
foreach
循环我们不在这里介绍。因为它需要用到一个新的东西,叫做数组(Array)。所以本节内容我们只讲前面三种循环语句。
Part 1 while
语句
最基础的语句就是 while
语句。while
语句的格式是这样的:
if
写法基本上是一样的,只是 if
改成了 while
while
和 if
的格式完全就只有关键字不一样,其它地方写法都是一样的。它的执行逻辑是这样的:我们首先从 i <= 100
这里开始判断。由于 i
从 1 开始,所以条件必然是成立的。按照条件要求,大括号里的代码就会执行一遍。因此,sum = sum + i
和 i++
都会执行一次。执行完毕后,会返回到 while
循环的条件(i <= 100
)处再次判断条件。由于 i
此时执行了一次 i++
变成了 2,所以和原来的数据不一样了。但是,2 依旧是满足 i <= 100
sum = sum + i
和 i++
。在反复执行到 i
超过 100 的时候,while
循环就不再执行了,直接到第 8 行,输出 sum
的结果。
如果你是初学编程的话,可能看起来会有点吃力。但是从逻辑上来说,你可以知道一点。循环体(就是大括号这一坨代码)里,
i
始终是一个单位一个单位地在增大。那么,从i
最开始的 1 开始,就会在内部反复执行到 101 才结束。为什么是 101 呢?因为条件i <= 100
是到 100 都满足的,因此第一个不满足的数值应该是 101 才对。另外,虽然到 101 才是对的,但内部循环体始终执行的次数是 100 次,即
i
是 1 的时候执行一次,是 2 的时候执行一次,3 的时候执行一次……直到i
是 100 的时候,还会执行一次。这一共是 100 次(特别需要注意的是,从 1 到 100 是一百次,不是 99 次)。最后,每一次执行,都会执行
sum = sum + i
这个规程,而每次都是记录了i
当前的最新数据(1、2、3、4 等等),因此sum
最终的结果一定是累计从 1 到 100 的总和。所以程序是没有问题的。
下面我们回头来看。sum = sum + i
可以改成 sum += i
,而 i++
写在前面那个语句 sum = sum + i
之后,且这个语句里也用到了 i
,所以我们还可以把 i++
合并到前面那个语句里。所以,循环体甚至可以简化成这样:
当然了。大括号只有这么一个语句的话,按照道理是可以不写大括号的。所以,第 3、5 行的大括号是可以不要的。
这样代码就会少很多。
请说明原因。
Part 2 do
-while
语句
由于有些时候,我们没有必要让用户最开始就判断数据是否需要循环,因此 C# 提供了一种后置条件的循环语句:do
-while
。它的格式有点别扭:
do
关键字就是为了起到一个占位的作用,用来框定循环体范围,没别的用。这种循环不论如何都会先执行一遍。执行完毕后,再来看条件是不是成立。注意,这里的条件写的是 ++i <= 100
,所以注意先执行 ++i
,然后才是条件判断。
可以看出,++i
这个语法格式是比较重要的。如果没有它的话,我们不论如何都无法做到在循环体执行完毕后立刻先让 i
增大一个单位,然后才是判断循环。不过确实,你也可以把 i++
写进循环里,不过逻辑嘛,你就自己理解了。
你可以这么写。当然,再浪一点的话,你还可以这么写:
只是一定要注意,do
-while
是一个整体形式的语句。因此在最后需要加分号。在之前的 if
、switch
和 while
do
-while
语句的特殊性,最后一个地方写的是条件表达式,因此一定要加分号。
Part 3 for
语句
最舒服的语法还得是 for
。前面两种循环我们都大概看了一下,应该没有大问题。不过 for
循环语句可能难度偏大一些,因为它的写法很灵活。
3-1 标准格式
如果我们改写一下从 1 到 100 的和,用 for
循环的话,我们可以这么写:
for
后面的小括号里是用分号分隔的每一组数据。从语义上理解的话,我们可以认为 for
循环是一种迭代循环。所谓的迭代,就是让一个实体对象作为基础参考,一直追踪它自身变化的过程。每次循环完毕后,这个东西都会变化,然后将变化后的结果继续带入下一次的运算过程。
比如说,我们完全可以只看 i
来找出变化过程。i
是从 1 到 100,一次增大一个单位,因此在小括号里我们写的是 int i = 1; i <= 100; i++
。最左边的是初始赋值语句,表示 i
从多少开始;中间这个写变量应该在什么范围区间里不断循环。显然这里我们需要写的是 i <= 100
,因为这样才能表示 i
在 100 以内都要反复执行循环。最后一个部分写的是 i++
,这正好对应了循环体执行完毕后自动增大一个单位的过程。最后,在循环体里,我们只需要关心 sum
的变化即可。这就是 for
循环的语义理解方式。
3-2 缺省迭代
在循环的时候,我们有时候不一定非得要写一些东西。如果我们需要把初始赋值提出来的话,我们可以这么搞:
如果我们想把增量写到循环体里,可以这么搞:
你甚至可以省略两部分:
你还可以玩骚操作,把循环体当增量写进增量里面去:
大括号都没东西了,干脆改成空语句
空语句就是一个以分号结尾的语句,这个语句里啥都不写,就写成一个分号
;
。这种语句本来是毫无意义的写法,但是为了保证for
for
的执行范围,保证编译器分析代码的时候不出问题。
3-3 复合迭代
在迭代的过程之中,for
循环还允许我们使用多个变量同时迭代。
比如这个例子里,i
和 j
变量同时声明到循环的小括号里。这个表示 i
和 j
同时赋初始数值;而后面的 i++
和 j += 2
是同时充当增量。在执行过程之中,i++
先执行,然后就是 j += 2
。
从这个角度来说,for
Part 4 死循环
既然 if
可以写永真条件,那么 while
这些循环呢?是的,我们完全可以这么做。
这种条件是 true
的永真式,我们就把这种循环叫做死循环(Dead Loop)。死循环是永远都不会自动退出的,只要它从上到下执行的时候遇到这种死循环了,就不会主动退出来。
Part 5 特殊循环控制语句
为了帮助辅助使用循环语句,我们可以在循环里插入一些特殊的语句,来灵活控制执行过程。
5-1 break
语句
这个单词是不是很熟悉?是的,就是之前 switch
语句最后跟的那个东西。它还有一个作用就是使得循环体的代码不再执行,自动跳出循环。
for
循环里嵌入了一个 if
语句,这表示每一次循环体开始执行的时候,都执行判断,是否 i % 3 == 0
。由于 i
从 1 到 5,因此会在判断之后,执行输出语句。一旦遇到 i % 3 == 0
条件成立的时候,就会自动执行 break
语句,整个 for
循环后面的代码都不再执行了,直接跳出整个循环。
5-2 continue
语句
和 break
语句相反,continue
一旦遇到 continue
语句,就会自动跳转到循环的条件判断,或者是增量处。比如说 while
循环,遇到 continue
语句的时候,就会自动跳转到 ++i <= 100
;而后者 for
循环,遇到 continue
的时候,就会自动跳转到 i++
5-3 死循环里使用循环控制语句
在完成了前面的内容的学习之后,我们来看看死循环怎么嵌套这种东西。
这里条件写的是
true
。实际上for
循环里的条件是可以不写的,默认是true
:for (; ; i++)
就等价于for (; true; i++)
,但while
和do
-while
不行。