1为什么要循环
程序一般是一句接一句、从上往下执行的;想让同一段代码重复执行很多遍,就要用循环。每次重复执行的那一块代码叫循环体。
2while 循环
while 先看条件,条件成立才执行循环体;执行完再回来看条件,直到条件不成立。
| 7 | int i = 1; | // i 从 1 开始 |
| 8 | while (i <= 3) | // 每圈先看条件:i 小于等于 3 吗? |
| 9 | { | // while 循环体开始 |
| 10 | Console.WriteLine("第 " + i + " 次"); | // i = 1、2、3 时各打印一行 |
| 11 | i++; | // i 加一(i++ 就是 i = i + 1);i 变成 4 时条件不成立 |
| 12 | } | // while 循环体结束,回到条件 |
| 13 | Console.WriteLine("循环结束"); | // 退出循环后执行 |
查看完整代码(15 行)
| 1 | using System; | // 引入 System 命名空间,Console 在这里 |
| 2 | | |
| 3 | class Program | // 定义 Program 类 |
| 4 | { | // Program 类开始 |
| 5 | static void Main() | // 程序入口 Main 方法 |
| 6 | { | // Main 开始 |
| 7 | int i = 1; | // i 从 1 开始 |
| 8 | while (i <= 3) | // 条件:i 小于等于 3 |
| 9 | { | // while 循环体开始 |
| 10 | Console.WriteLine("第 " + i + " 次"); | // 打印第几次 |
| 11 | i++; | // i 加一 |
| 12 | } | // while 循环体结束 |
| 13 | Console.WriteLine("循环结束"); | // 循环后面的语句 |
| 14 | } | // Main 结束 |
| 15 | } | // Program 类结束 |
第 1 次 第 2 次 第 3 次 循环结束
很多人会忘了在循环体里改变 i。去掉 i++,i 永远是 1,条件永远成立,“第 1 次”会一直打印下去,这就是无限循环:
| 8 | while (i <= 3) | // i 一直是 1,条件永远成立 |
| 9 | { | // while 循环体开始 |
| 10 | Console.WriteLine("第 " + i + " 次"); | // 一直打印“第 1 次” |
| 11 | } | // while 循环体结束:没有 i++,i 不会变 |
3do...while 循环
do...while 正好反过来:先执行循环体,最后才看条件,所以循环体至少执行一次。下面 n 是 5,两个循环的条件都是 n < 3。
| 8 | while (n < 3) | // while 先看条件:5 < 3 不成立 |
| 9 | { | // while 循环体开始 |
| 10 | Console.WriteLine("while 执行了"); | // 一次也不执行 |
| 11 | n++; | // 一次也不执行 |
| 12 | } | // while 循环体结束 |
| 13 | do | // do:先执行,不看条件 |
| 14 | { | // do 循环体开始 |
| 15 | Console.WriteLine("do...while 执行了"); | // 先打印一次 |
| 16 | n++; | // n 变成 6 |
| 17 | } while (n < 3); | // 最后看条件:6 < 3 不成立,退出(注意结尾有分号) |
do...while 执行了
4for 循环
for 把管理循环变量的三件事写在同一行,用分号隔开:初始化(开始时做一次)、条件(每圈开始前检查)、更新(每圈结束后执行)。三件事写在一起,就不容易忘了改变量。
| 7 | for (int i = 1; i <= 3; i++) | // 初始化 i = 1;条件 i <= 3;每圈结束后 i++ |
| 8 | { | // for 循环体开始 |
| 9 | Console.WriteLine("第 " + i + " 次"); | // 打印第 1、2、3 次 |
| 10 | } | // for 循环体结束 |
| 11 | Console.WriteLine("循环结束"); | // i 变成 4 后退出,执行这一行 |
输出和上面的 while 完全一样。
5foreach 循环
要把一组数据挨个过一遍,用 foreach 更省事:不用自己数次数,它把值依次取出来,取完自动结束。
| 7 | string[] fruits = { "苹果", "香蕉", "橙子" }; | // 字符串数组:三个水果名放在一起(数组后面专门讲) |
| 8 | foreach (string f in fruits) | // 每一圈,f 拿到下一个名字 |
| 9 | { | // foreach 循环体开始 |
| 10 | Console.WriteLine(f); | // 打印这一圈拿到的名字 |
| 11 | } | // foreach 循环体结束:三个都取完就结束 |
6嵌套循环
循环里面再放循环叫嵌套循环,while、for、do...while、foreach 都可以互相嵌套。下面的小九九,外层 i 管第几行,内层 j 管这一行写几个。
| 7 | for (int i = 1; i <= 3; i++) | // 外层:i 从 1 到 3,一圈一行 |
| 8 | { | // 外层循环体开始 |
| 9 | for (int j = 1; j <= i; j++) | // 内层:j 从 1 数到 i,i 是几就写几个 |
| 10 | { | // 内层循环体开始 |
| 11 | Console.Write(j + "×" + i + "=" + (i * j) + " "); | // 打印 j×i=积 和两个空格;Write 不换行 |
| 12 | } | // 内层循环体结束 |
| 13 | Console.WriteLine(); | // 一行写完,换行 |
| 14 | } | // 外层循环体结束 |
1×1=1 1×2=2 2×2=4 1×3=3 2×3=6 3×3=9
外层跑了三圈,内层一共跑了 1 + 2 + 3 = 6 次。
7循环体里的变量
循环体是一对花括号围起来的一块代码,在里面声明的变量,出了循环就不能用了:
| 7 | for (int i = 1; i <= 3; i++) | // 循环三圈 |
| 8 | { | // for 循环体开始 |
| 9 | int square = i * i; | // square 在循环体里声明 |
| 10 | } | // for 循环体结束,square 到这里为止 |
| 11 | Console.WriteLine(square); | // 在循环外面用 square |
Program.cs(11,27): error CS0103: 当前上下文中不存在名称“square”
square 出了花括号就不能用,这在编译时就定了。至于用 new 创建的对象,C# 不会在离开花括号时马上销毁它,而是由垃圾回收器(.NET 自动清理没人用的内存的机制)以后回收;“离开范围就销毁对象”是 C++ 的规则。
8break:结束整个循环
接下来是两个循环控制语句:break 和 continue,它们让循环不按平常的顺序走。break 立刻结束整个循环,程序接着执行循环后面的语句;break 也能用在 switch 里。
| 7 | for (int i = 1; i <= 10; i++) | // 本来要数到 10 |
| 8 | { | // for 循环体开始 |
| 9 | if (i == 4) | // i 等于 4 时 |
| 10 | { | // if 块开始 |
| 11 | break; | // 立刻结束整个 for 循环 |
| 12 | } | // if 块结束 |
| 13 | Console.WriteLine(i); | // 只打印出 1、2、3 |
| 14 | } | // for 循环体结束 |
| 15 | Console.WriteLine("循环结束"); | // break 后跳到这里 |
1 2 3 循环结束
循环套着循环时,break 只跳出它所在的最内层循环,外层照样继续。下面这段内层循环外面还套着一层 for (:
| 9 | for (int j = 1; j <= 3; j++) | // 内层:j 从 1 到 3 |
| 10 | { | // 内层循环体开始 |
| 11 | if (j == 2) | // j 等于 2 时 |
| 12 | { | // if 块开始 |
| 13 | break; | // 只跳出内层 for,外层继续下一圈 |
| 14 | } | // if 块结束 |
| 15 | Console.WriteLine("i=" + i + ", j=" + j); | // 每圈只来得及打印 j = 1 |
i=1, j=1 i=2, j=1 i=3, j=1
9continue:跳过这一圈
continue 不结束循环,只跳过这一圈剩下的语句。在 for 循环里,跳过以后还会先执行 i++,再检查条件。
| 7 | for (int i = 1; i <= 5; i++) | // i 从 1 到 5 |
| 8 | { | // for 循环体开始 |
| 9 | if (i == 3) | // i 等于 3 时 |
| 10 | { | // if 块开始 |
| 11 | continue; | // 跳过下面的打印,去做 i++,i 变成 4 |
| 12 | } | // if 块结束 |
| 13 | Console.WriteLine(i); | // 打印 1、2、4、5 |
| 14 | } | // for 循环体结束 |
换成 while 就藏着一个坑:i++ 在循环体最后,continue 直接跳回条件,i++ 被跳过,i 永远停在 3,又成了无限循环。打印出 1 和 2 以后就再也没有动静,程序也不结束:
| 8 | while (i <= 5) | // 条件:i 小于等于 5 |
| 9 | { | // while 循环体开始 |
| 10 | if (i == 3) | // i 等于 3 时 |
| 11 | { | // if 块开始 |
| 12 | continue; | // 直接跳回条件,下面的 i++ 被跳过 |
| 13 | } | // if 块结束 |
| 14 | Console.WriteLine(i); | // 只打印了 1、2 |
| 15 | i++; | // i 是 3 时永远走不到这一行 |
| 16 | } | // while 循环体结束 |
1 2
改法是在 continue 前面先把 i 加一,输出就和 for 循环一样是 1、2、4、5:
| 10 | if (i == 3) | // i 等于 3 时 |
| 11 | { | // if 块开始 |
| 12 | i++; | // 先把 i 加一,变成 4 |
| 13 | continue; | // 再跳回条件 |
| 14 | } | // if 块结束 |
10无限循环
条件永远成立,循环就停不下来。for 括号里的三个部分都可以省略,只剩两个分号;条件不写就当它永远为真:
| 10 | for (; ; ) | // 初始化、条件、更新全空着,条件当成永远为真 |
| 11 | { | // for 循环体开始 |
| 12 | Console.WriteLine("Hey! I am Trapped"); | // 不停地打印这句英文,停不下来 |
| 13 | } | // for 循环体结束 |
写成 while ( 效果完全一样。真正的程序里,无限循环通常配合 break,条件满足时跳出来。下面打印 1、2、3 后 break,接着执行循环后面的“跳出来了”:
| 7 | int n = 0; | // n 从 0 开始 |
| 8 | while (true) | // 条件永远为真 |
| 9 | { | // while 循环体开始 |
| 10 | n++; | // 每圈给 n 加一 |
| 11 | Console.WriteLine(n); | // 打印 1、2、3 |
| 12 | if (n == 3) | // n 等于 3 时 |
| 13 | { | // if 块开始 |
| 14 | break; | // 跳出循环 |
| 15 | } | // if 块结束 |
| 16 | } | // while 循环体结束 |
11小结
- while 先看条件再执行;do...while 先执行一次再看条件。
- for 把初始化、条件、更新写在一起;foreach 把值挨个取出来;循环里还能套循环。
- break 结束它所在的那一层循环;continue 跳过这一圈剩下的部分,用 while 时别把
i++跳过了。 - 条件永远成立,就成了无限循环。
下一节:类里的数据,谁能看、谁能改?
