It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.
循环
本章将介绍 C 语言的重复语句(迭代语句) ,这种语句允许用户设置循环。
循环 (loop)时重复的执行其他语句(循环体 )的一种语句。在 C 语言中,每个循环都有自己的控制表达式 (controlling expression)。循环每执行一次,都要对控制表达式求值。如果表达式为真(表达式的值非 0),那么循环继续执行;否则,退出循环。
C 提供了三种迭代语句:
while 语句
do 语句(do while 语句)
for 语句
其中以 for 语句最为常用。
本节后面还会讨论循环相关的 C 语言特性。如 break,continue,goto 语句
一 while 语句
1. while 的基本用法
while 的基本格式如下:
执行 while 语句时,首先计算控制表达式的值。如果值不为零,那么执行循环体,接着再次判定 控制表达式的真值,如果为真,再次执行循环体。直到控制表达式的真值为假,才会结束 while 语句。
例1-1: 倒计数程序
1 2 3 4 5 int i = 10 ;while (i > 0 ){ printf ("%d\n" , i); i--; }
关于这个例子,我们可以对 while 进行深度思考:
while 循环终止时,控制表达式的值一定为假 。
1 2 3 4 5 6 int i = 10 ;while (i > 0 ){ printf ("%d\n" , i); i--; }printf ("%d" , i);
可能根本不执行 while 循环体 。
1 2 3 4 5 6 int i = 0 ;while (i > 0 ){ printf ("%d\n" , i); i--; }
while 语句常有多种写法
1 2 3 4 int i = 10 ;while (i > 0 ){ printf ("%d\n" , i--); }
2. 无限循环
如果控制表达式的值始终非零,while 循环将无法终止。
1 2 3 while (1 ){ printf ("Hello World\n" ); }
除非循环体内有控制循环的语句(break,return,goto)或者调用了导致程序终止的函数,非则上面的循环永远不会结束。
程序 1:显示平方表
现编写一个程序来显示平方表。首先程序提示用户输入一个数 n,然后显示出 n 行的输出,每行包含 一个 1 ~ n 的数及其平方值。
1 2 3 4 5 6 7 This program prints a table of squares. Enter number of entries in table: 5 1 1 2 4 3 9 4 16 5 25
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main (void ) { int n; int i = 1 ; printf ("This program prints a table of squares.\n" ); printf ("Enter number of entries in table: " ); scanf ("%d" , &n); while (i <= n){ printf ("\t%d\t%d\n" , i, i * i); i++; } return 0 ; }
程序 2:数列求和
1 2 3 This program sums a series of integers. Enter integers (0 to terminate ) :8 23 71 5 0 The sum is:107
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main (void ) { int n, sum = 0 ; printf ("This program sums a series of integers.\n" ); printf ("Enter integers(0 to terminate): " ); scanf ("%d" , &n); while (n != 0 ){ sum += n; scanf ("%d" , &n); } printf ("The sum is:%d\n" , sum); return 0 ; }
我写的,仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> int main (void ) { int n, sum = 0 ; printf ("This program sums a series of integers.\n" ); printf ("Enter integers(0 to terminate): " ); while (scanf ("%d" , &n) && n){ sum += n; } printf ("The sum is:%d\n" , sum); return 0 ; }
或者 while 循环可以这样写:
1 2 3 4 5 6 int n = 1 , sum = 0 ;while (n != 0 ){ scanf ("%d" , &n); sum += n; }
二 do 语句
1. do while 基本用法
do 语句 和 while 语句其实本质上是相同的。只不过 do 语句至少会执行一次 循环体。
基本形式:
例2-1 倒计数程序
1 2 3 4 5 int i = 10 ;do { printf ("%d\n" , i); i--; }while (i > 0 );
顺便一提,do 语句最好都加上花括号 。
虽然 do while 没有 while 语句使用的那么多,但是前者对于至少需要执行一次 的循环来说是十分方便的。
程序 3:编写一个程序计算用户输入的整数的位数
1 2 Enter a integer: 60 The number has 2 digit(s).
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main (void ) { int n, count = 0 ; printf ("Enter a nonnegative integer:" ); scanf ("%d" , &n); do { count++; n /= 10 ; }while (n != 0 ); printf ("The number has %d digit(s).\n" , count); return 0 ; }
如果我们用 while 循环:
1 2 3 4 while (n != 0 ){ count++; n /= 10 ; }
如果你输入的是 0 ,这个循环会直接退出。这不符合我们的预期,0 也是整数啊,而且它有 1 位数。
三 for 循环
现在开始介绍 C 语言最后一种循环,也是功能最强大 的一种循环:for 语句。它是我们用的最多的一种循环,一定要熟练掌握。
1. for 语句的基本用法
1 2 3 for (表达式1 ; 表达式2 ; 表达式3 ){ 循环体 }
例3-1 倒计数程序
1 2 3 for (i = 10 ; i > 0 ; i--){ printf ("%d\n" , i); }
在执行上面这个 for 语句时,i 先初始化为 10;然后判定 i 是否大于 0 ;如果结果为真,执行循环体;然后对变量 i 进行自减操作;然后再次判断 i 是否大于 0 … 直到最后一次 i 自减后,i > 0 不成立了,退出循环。
for 循环如果我们用 while 语句 也可以模拟:
1 2 3 4 5 i = 10 ;while (i > 0 ){ printf ("%d\n" , i); i--; }
抽象一下即为:
1 2 3 4 5 表达式1 ;while (表达式2 ){ 循环体; 表达式3 ; }
for 循环中的 i--
可以写成 --i
吗?
即:
1 2 3 for (i = 10 ; i > 0 ; --i){ printf ("%d\n" , i); }
这种做法对循环没有任何影响。i++
和 i--
在 for 循环的 表达式3 中是完全等价的。
2. 在 for 语句中省略表达式
1.省略第一个表达式
1 2 3 4 i = 10 ;for (; i > 0 ; --i){ printf ("%d\n" , i); }
注意:
省略第一个表达式相当于不初始化 i
即便 第一个表达式省略了,也不能省略后面的分号
2. 省略第三个表达式
1 2 3 4 for (i = 10 ; i > 0 ;){ printf ("%d\n" , i); --i }
3. 省略第一个和第三个表达式
1 2 3 4 5 i = 10 ;for (; i > 0 ; ){ printf ("%d\n" , i); --i }
是不是很像我们的 while 语句?
4. 省略第二个表达式
1 2 3 for (; ;) { printf ("Hello\n" ); }
省去控制表达式,默认为真 ,因此 for 语句会不断循环下去。
它相当于:
1 2 3 while (1 ) { printf ("Hello\n" ); }
3. C99 中的 for 语句
C99 中第一个表达式可以替换成一个声明:
1 2 3 for (int i = 0 ; i < 10 ; i++){ }
变量 i 不需要在该句之前进行声明。事实上,如果变量 i 在之前已经声明了,这个语句会创建一个新的 i 且该值仅用于循环内 。(新的 i 会覆盖之前的 i 。for 语句的花括号相当于一个块,新 i 的作用域仅在这个块内。)
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 int main (void ) { int i = 20 ; for (int i = 0 ; i < 10 ; i++) { printf ("%d\n" , i); } printf ("%d\n" , i); return 0 ; }
输出:
1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 6 7 8 9 20
最后输出的 20 不符合我们的预期,这说明了一个问题:
旧的 i 始终存在,只不过在 for 语句内,新的 i 相对旧的 i 显“显性”(可以用高中生物的基因来理解一下;或者你就理解为覆盖,但是新的 i 开辟的是新的内存,它不会真的覆盖旧的 i )。
for 语句声明的变量不可以在循环外访问(生存期仅在 for 语句内)。例如:
1 2 3 4 5 for (int i = 0 ; i < 10 ; i++) { printf ("%d\n" , i); }printf ("%d\n" , i);
让 for 语句声明自己的循环控制变量通常是一个好办法:这样方便且程序的可读性更强 ,但是如果在 for 循环退出后还要使用该变量 ,则只能使用以前的 for 语句格式。
另外,for 语句可以声明多个变量 ,只要它们的类型相同 :
1 2 3 for (int i = 0 , j = 10 ; i < j; i++, j--){ }
4. 逗号表达式
逗号表达式(comma expression)
基本用法
1 表达式1 ,表达式2 ,表达式3 ,...,表达式n;
逗号表达式的优先级低于所有其他运算符
从左向右依次执行:先计算表达式1,然后是表达式2 …
最终整个逗号表达式的值为最后一个表达式的值
例如:
1 i = 1 , j = 2 , k = i + j;
相当于是:
1 ((i = 1 ), (j = 2 ), (k = i + j));
整个逗号表达式的值为 k = i + j 的值,也就是 3
应用
假如在进入 for 语句时希望初始化两个变量
1 2 3 4 sum = 0 ;for (i = 1 ; i <= N; i++){ sum += i; }
改写为:
1 2 3 for (sum = 0 , i = 1 ; i <= N; i++){ sum += i; }
程序 4:显示平方表(for)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> int main (void ) { int n; printf ("This program prints a table of squares.\n" ); printf ("Enter number of entries in table: " ); scanf ("%d" , &n); for (int i = 1 ; i <= n; i++){ printf ("\t%d\t%d\n" , i, i * i); } return 0 ; }
程序 5:不用乘法,实现程序“显示平方表”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main (void ) { int n; printf ("This program prints a table of squares.\n" ); printf ("Enter number of entries in table: " ); scanf ("%d" , &n); for (int i = 1 , square = 1 , odd = 3 ; i <= n; i++){ printf ("\t%d\t%d\n" , i, square); square += odd; odd += 2 ; } return 0 ; }
for loop的嵌套运用尤为突出,请各位同学掌握!📝
输出以下4×5的矩阵
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> int main () { int i,j,n=0 ; for (i=1 ;i<=4 ;i++) for (j=1 ;j<=5 ;j++,n++) { if (n%5 ==0 ) printf ("\n" ); printf ("%d\t" ,i*j); } printf ("\n" ); return 0 ; }
本程序包括一个双重循环,是for循环的嵌套。外循环变量i由1变到4,用来控制输出4行数据;内循环变量j由1变到5,用来控制输出每行中的5个数据。
四 退出循环
有时,循环还没有结束,但我们需要在循环的中间设置退出点 ,甚至可能需要对循环设置多个退出点。break 语句可以满足这种需求。
continue 语句用来跳过某次迭代的部分内容,但是不会跳过整个循环。
goto 语句允许程序从一条语句直接跳到另一条语句。
1. break 语句
break 语句不但可以把程序控制从 switch 语句中转移出来;还可以用于跳出 while,do while,和 for 循环。
程序 6:判断素数
假如要编写一个程序判断 n 是否为素数。我们计划是编写一个 for 语句用 n 除以 2 到 n - 1 之间的所有数。一旦发现有约数就跳出循环,没不需要继续尝试下去。循环终止后,可以用 if 判断循环是提前终止(不是素数)还是正常终止(是素数)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> int main (void ) { int n, i; printf ("Enter a number:" ); scanf ("%d" , &n); for (i = 2 ; i < n; i++){ if (n % i == 0 ){ break ; } } if (i < n){ printf ("%d is not a prime\n" , n); }else { printf ("%d is a prime\n" , n); } return 0 ; }
程序 7:程序2 重写
程序 2 中,要求我们当用户输入 0 时,结束求和。我们用很多中方法去实现了,我们可以用 break 语句让程序的意图更加直观。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> int main (void ) { int n, sum = 0 ; printf ("This program sums a series of integers.\n" ); printf ("Enter integers(0 to terminate): " ); while (1 ){ scanf ("%d" , &n); if (n == 0 ){ break ; }else { sum += n; } } printf ("The sum is:%d\n" , sum); return 0 ; }
一个 break 只能跳出一个循环 ,如果循环嵌套,那么可能需要多个 break 才能从里层的循环跳出。
1 2 3 4 5 6 7 8 9 int i;while (1 ){ for (i = 1 ; i < 10 ; i++){ if (i == 5 ){ printf ("%d\n" , i); break ; } } }
运行一下这个程序,发现,屏幕上一直在输出 5,说明外层的循环没有跳出。
2. continue 语句
break 语句刚好将程序控制转移到循环体的末尾之后 ;而 continue 语句刚好将程序控制转移到循环体末尾之前 。
break 语句会跳出循环;而 continue 语句会将程序控制留在循环体之内。
其实 continue 的作用就是跳过本次循环点剩下内容,重新开始下一轮循环。
程序 8:计算奇数和
编写一个程序。先提示用户输入一个数 n,然后将 1 ~ n 的所有奇数相加,然后输出最终的和
不使用 continue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main (void ) { int n, sum = 0 ; printf ("Enter a number:" ); scanf ("%d" , &n); for (int i = 1 ; i <= n; i++){ if (i % 2 != 0 ){ sum += i; } } printf ("Sum of odds is :%d\n" , sum); return 0 ; }
使用 continue
只需要改变 for 循环即可
1 2 3 4 5 6 for (int i = 1 ; i <=n; i++){ if (i % 2 == 0 ){ continue ; } sum += i; }
3. goto 语句
break 和 continue 语句都是跳转语句:它们可以把控制从程序的一个位置转移到另一个位置。然而,这两者都是受限制的。
goto 语句则可以跳转到函数中任何有标号的语句处。(C99 增加了一条限制:goto 不能用于绕过变长数组 (后面的章节会讲)的声明。)
goto 语句在早期的编程语言中很常见,但是日常 C 语言编程却很少使用它了。break,continue,return 语句(本质上都是受限制的goto 语句)和 exit 函数足以应付大多数需要使用 goto 的情况。
而且 goto 语句不建议滥用,能不用就不用 ,因为 goto 语句可以打乱程序原本的执行顺序,这大大降低了程序的可读性,提高了改错成本。
但是 goto 语句偶尔还是有用的,比如上面的例子:循环嵌套的情况,使用goto 语句就很好跳出多重循环了:
1 2 3 4 5 6 7 8 9 10 int i;while (1 ){ for (i = 1 ; i < 10 ; i++){ if (i == 5 ){ printf ("%d\n" , i); goto find; } } } find:
程序:账薄结算
这个程序帮你理解一种简单的交互式程序设计,我们可以通过这种方式设计菜单。
题目:
开发一个程序用来维护账簿的余额。程序将为用户提供选择菜单:清空余额账户,向账户存钱,从账户取钱,显示当前余额,退出程序。选项用 0,1,2,3,4表示。程序的会话类似这样:
1 2 3 4 5 6 7 8 9 10 11 **** ACME checkbook-balancing program **** Comands: 0 = clear, 1 = credit, 2 = debit, 3 = balance, 4 = exit Enter command: 1 Enter amount of credit: 1042.56 Enter command: 2 Enter amount of debit : 133.56 Enter command: 3 Current balance: 909 Ener command: 4 Goodbye~
参考程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <stdio.h> int main (void ) { double balance = 0 ; double credit, debit; printf ("**** ACME checkbook-balancing program ****\n" ); printf (" Comands: \n" ); printf (" 0 = clear \n" ); printf (" 1 = credit \n" ); printf (" 2 = debit \n" ); printf (" 3 = balance \n" ); printf (" 4 = exit \n" ); while (1 ) { int choice; printf ("Enter command: " ); scanf ("%d" , &choice); switch (choice) { case 0 : printf ("Are you sure to clear your balance?\n" ); printf ("1 = yes, 0 = no\n" ); int isClear; scanf ("%d" , &isClear); if (isClear == 1 ) { balance = 0 ; printf ("clear successfully!\n" ); } break ; case 1 : printf ("Enter amount of credit: " ); scanf ("%lf" , &credit); balance += credit; break ; case 2 : printf ("Enter amount of debit : " ); scanf ("%lf" , &debit); balance -= debit; break ; case 3 : printf ("Current balance: %.2f\n" , balance); break ; case 4 : printf ("Are you sure to quit?\n" ); printf ("1 = yes, 0 = no\n" ); int isQuit; scanf ("%d" , &isQuit); if (isQuit == 1 ) { printf ("Goodbye~\n" ); return 0 ; } else { break ; } default : printf ("Illeagl option!\n" ); break ; } } }
五 空语句
第二个语句就是一个空语句:除了末尾的分号,什么符号也没有。
空语句主要有一个好处:编写空循环体的循环。
判断素数的循环我们可以这样改写:
1 2 for (d = 2 ; d <= n && n % d != 0 ; d++) ;
注意不要写成:
1 for (d = 2 ; d <= n && n % d != 0 ; d++) ;
程序员习惯将空语句单独放在一行 ,否则,一般人阅读程序时可能会混淆后面的语句是否是其循环体。
1 2 3 for (d = 2 ; d <= n && n % d != 0 ; d++) ;if (d < n) printf ("%d is divisible by %d" , n, d);
一般情况下,将普通循环转化为带空循环体的语句不会提高效率 ,但是会让程序更加简洁。
但是在一些情况下,可能带空循环体的循环会更加高效。如:读取字符数据时(后面会讲)。
空语句可能会造成的情况
1.if 语句
1 2 if (d == 0 ); printf ("Error: division by zero\n" );
如果输入的 d 不是 0,后面的这条消息一样会被打印。
2.while 语句
1 2 3 4 5 i = 10 ;while (i > 0 );{ printf ("%d\n" , i); i--; }
程序会进入死循环,因为 while 没有循环体,而 i 的的值不会减小。
1 2 3 4 5 i = 11 ;while (--i > 0 );{ printf ("%d\n" , i); i--; }
循环终止,但是花括号内的语句只被执行一次。
输出:0
3. for 语句
1 2 for (i = 10 ; i > 0 ; i--); printf ("%d\n" , i);
printf 语句被执行一次
输出:0
参考资料:《C Primer Plus》《C语言程序设计:现代方法》