/* summing.c -- 交互式求整数和 */#include <stdio.h>int main(void){ long num; long sum = 0L; /* 和初始化为 0 */ int status; printf("Please enter an integer to be summed "); printf("(q to quit): "); status = scanf("%ld", &num); while (status == 1) /* == 表示"是否相等" */ { sum = sum + num; printf("Please enter next integer (q to quit): "); status = scanf("%ld", &num); } printf("Those integers sum to %ld.\n", sum); return 0;}
示例运行:输入 44、33、88、121 后敲 q,输出 Those integers sum to 286.。
循环体内必须有东西能改变测试表达式的值,让它最终变假,否则就是死循环:index = 1; while (index < 5) printf("Good morning!\n"); 会无限打印——循环体里没有任何东西改变 index。若改成 while (--index < 5),index 往反方向跑,也未必是好事。
循环何时退出? 只在测试条件被求值时决定。清单 6.2(when.c)演示:n 在第二轮循环体内变成 7,但程序并不立刻退出,而是完成本轮循环,直到第三次求值 n < 7 才退出。
while:入口条件循环
while 是入口条件循环(entry-condition loop):必须先满足条件才进入循环体。条件一开始就为假,循环体一次都不执行:index = 10; 时 while (index++ < 5) 的循环体一次也不会跑;改成 index = 3; 就会执行。
语法要点:只有一条语句属于循环
程序清单 6.3 while1.c 是个反面教材:
/* while1.c -- 小心花括号 *//* 糟糕的写法制造了无限循环 */#include <stdio.h>int main(void){ int n = 0; while (n < 3) printf("n is %d\n", n); n++; printf("That's all this program does\n"); return 0;}
虽然 n++; 有缩进,但它没被花括号包住——while 只包含测试条件后的第一条语句。n 永远是 0,条件永远为真,程序无限打印 n is 0 直到被手动杀掉。
分号放错位置是另一个坑(清单 6.4 while2.c):while (n++ < 3); 里测试条件后紧跟的分号构成空语句(null statement)——它就是循环体!循环空转 3 次把 n 加到 4,其后的 printf() 根本不在循环里,输出只有 n is 4。
空语句与缩进骗局
while 的循环体是”测试条件后到第一个分号(或配对右花括号)为止”的一条语句。缩进只给人看,编译器只认花括号和分号。偶尔空语句是有意为之(如 while (scanf("%d", &num) == 1)\n ; /* 跳过整数输入 */),此时应把分号单独放一行并加注释,表明这是故意的。
6.3 哪个更大:关系运算符和表达式
while 循环常用**关系表达式(relational expression)**做测试。完整的关系运算符如下:
芝诺(Zeno)辩称飞矢永远到不了靶心:先飞一半,再飞剩下的一半的一半……无穷无尽。用时长表示就是无穷级数 1 + 1/2 + 1/4 + 1/8 + ...。清单 6.14 zeno.c 在一个 for 里用三个逗号同时初始化 time, x, t_ct、又用逗号同时更新 t_ct++, x *= 2.0,循环体只有两行:time += 1.0/x; 和打印。输入 15 项,部分和一路逼近 2.0(1.000000 → 1.500000 → 1.750000 → …… → 1.999969)。数学上可证级数收敛于 2:设 S 为和,S - S/2 = 1,故 S = 2。教训:动手算复杂级数前,先看看数学家有没有现成结论。
6.9 出口条件循环:do while
while 和 for 都是入口条件循环,条件不满足时循环体可能一次都不执行。C 还有出口条件循环(exit-condition loop):每轮执行之后才测试条件,保证至少执行一次——这就是 do while。
程序清单 6.15 do_while.c
/* do_while.c -- 出口条件循环 */#include <stdio.h>int main(void){ const int secret_code = 13; int code_entered; do { printf("To enter the triskaidekaphobia therapy club, \n"); printf("please enter the secret code number: "); scanf("%d", &code_entered); } while (code_entered != secret_code); printf("Congratulations! You are cured!\n"); return 0;}
用户必须猜中暗号 13 才能退出”恐惧 13 治疗俱乐部”。若用 while 改写(清单 6.16 entry.c),就得在循环前先写一遍提示和 scanf(),代码更长。一般形式(结尾的分号不能少):
do statementwhile ( expression );
使用建议:do while 只该用在”必须至少执行一次”的场合(比如密码程序——先提示输入才有得测)。千万别把”问用户是否继续”放进 do 里、“干正事”放进循环体——用户答”否”之后,正事还是会被干一遍,因为测试来得太迟了。
// scores_in.c -- 用循环处理数组#include <stdio.h>#define SIZE 10#define PAR 72int main(void){ int index, score[SIZE]; int sum = 0; float average; printf("Enter %d golf scores:\n", SIZE); for (index = 0; index < SIZE; index++) scanf("%d", &score[index]); // 读入 10 个成绩 printf("The scores read in are as follows:\n"); for (index = 0; index < SIZE; index++) printf("%5d", score[index]); // 回显输入 printf("\n"); for (index = 0; index < SIZE; index++) sum += score[index]; // 求和 average = (float) sum / SIZE; // 计算平均 printf("Sum of scores = %d, average = %.2f\n", sum, average); printf("That's a handicap of %.0f.\n", average - PAR); return 0;}
// power.c -- 计算数的整数次幂#include <stdio.h>double power(double n, int p); // ANSI 原型int main(void){ double x, xpow; int exp; printf("Enter a number and the positive integer power"); printf(" to which\nthe number will be raised. Enter q"); printf(" to quit.\n"); while (scanf("%lf%d", &x, &exp) == 2) { xpow = power(x, exp); // 函数调用 printf("%.3g to the power %d is %.5g\n", x, exp, xpow); printf("Enter next pair of numbers or q to quit.\n"); } printf("Hope you enjoyed this power trip -- bye!\n"); return 0;}double power(double n, int p) // 函数定义{ double pow = 1; int i; for (i = 1; i <= p; i++) pow *= n; return pow; // 返回 pow 的值}
示例运行:输入 1.2 12 输出 1.2 to the power 12 is 8.9161;输入 2 16 输出 2 to the power 16 is 65536;输入 q 退出。要点解读:
算法:pow 初始化为 1,循环 p 次每次 pow *= n,n 自乘 p 次即得 n 的 p 次方;
函数三连:double power(double n, int p); 是原型(声明返回 double、接受两参数,末尾有分号);xpow = power(x, exp); 是调用;double power(double n, int p) { ... } 是定义(头部不带分号);