第六章 循环控制结构
循环结构:需要重复执行的操作
被重复执行的语句序列称为循环体
- 计数控制的循环
- 条件控制的循环
- 当型循环结构
- 直到型循环结构
- for
- while
- do-while
while(循环控制表达式) { 语句序列 }
计算循环控制表达式的值,如果循环控制表达式的值为真,执行循环体中的语句,返回;如果循环控制表达式的值为假,退出循环。
do { 语句序列 }while(循环控制表达式);
执行循环体中的语句,计算循环控制表达式的值,如果循环控制表达式的值为真,返回;如果循环控制表达式的值为假,退出循环。
for(初始化表达式;循环控制表达式;增值表达式) { 语句序列 }
初始化表达式的作用是为循环控制变量赋初值,决定了循环的起始条件
如何对循环变量进行增值,决定了循环的执行次数
如果在循环体内再次改变这个变量的值,将改变循环正常的执行次数
//L6-1
#include <stdio.h> main() { int i, n, sum; printf("Input n:"); scanf("%d", &n); sum = 0; /* 累加和变量初始化为0 */ for (i=1; i<=n; i++) { sum = sum + i; /* 做累加运算 */ } printf("sum = %d ", sum); }
逗号运算符:可把多个表达式连接在一起,作用是实现对各个表达式的顺序求值
空语句(仅由一个分号构成):表示什么也不做,常用于编写延时程序
//L6-2
#include <stdio.h> main() { int i, n; long p = 1; /* 因阶乘值取值范围较大,故p定义为长整型,并赋初值1 */ printf("Please enter n:"); scanf("%d", &n); for (i=1; i<=n; i++) { p = p * i; /* 做累乘运算 */ } printf("%d! = %ld ", n, p); /* 以长整型格式输出n的阶乘值 */ }
//L6-3
#include <stdio.h> main() { int i, n; long p = 1; printf("Please enter n:"); scanf("%d", &n); for (i=1; i<=n; i++) { p = p * i; printf("%d! = %ld ", i, p); /* 输出1~n之间的所有数的阶乘值 */ } }
//L6-4(嵌套循环)
#include <stdio.h> main() { int i, j, n; long p, sum = 0; /* 累加求和变量sum初始化为0 */ printf("Input n:"); scanf("%d", &n); for (i=1; i<=n; i++) { p = 1; /* 每次循环之前都要将累乘求积变量p赋值为1 */ for (j=1; j<=i; j++) { p = p * j; /* 累乘求积 */ } sum = sum + p; /* 累加求和 */ } printf("1!+2!+…+%d! = %ld ", n, sum); }
//运行结果 Input n:4 1!+2!+…+4! = 33
编写累加求和程序的关键在于寻找累加项的构成规律
- 当累加项较为复杂或者前后项之间无关时,需要单独计算每个累加项
- 当累加项的前项和后项之间有关时,可以根据关系通过前项来计算后项
//6-5
#include <stdio.h> main() { int i, j; for (i=0; i<3; i++) /* 控制外层循环执行3次 */ { printf("i=%d: ", i); for (j=0; j<4; j++) /* 控制内层循环执行4次 */ { printf("j=%d ", j); } printf(" "); } }
//运行结果 i=0: j=0 j=1 j=2 j=3 i=1: j=0 j=1 j=2 j=3 i=2: j=0 j=1 j=2 j=3
//6-6(条件控制的循环)
#include <stdlib.h> #include <stdio.h> main() { int magic; /* 计算机"想"的数 */ int guess; /* 用户猜的数 */ magic = rand(); /* 调用随机函数"想"一个数magic */ printf("Please guess a magic number:"); scanf("%d", &guess); /* 输入用户猜的数guess */ if (guess > magic) /*若guess>magic,则提示"Wrong!Too big"*/ printf("Wrong! Too big! "); else if (guess < magic)/*若guess<magic,则提示"Wrong!Too small"*/ printf("Wrong! Too small! "); else /* 否则提示"Right!"并打印这个数 */ printf("Right! "); }
//6-7
#include <time.h> /* 将函数time()所需要的头文件time.h包含到程序中 */ #include <stdlib.h> #include <stdio.h> main() { int magic, guess, counter = 0; srand(time(NULL)); /* 为函数rand()设置随机数种子 */ magic = rand() % 100 + 1; do{ printf("Please guess a magic number:"); scanf("%d", &guess); counter ++; if (guess > magic) printf("Wrong! Too big! "); else if (guess < magic) printf("Wrong! Too small! "); else printf("Right! "); }while (guess != magic); printf("counter = %d ", counter); }
//运行结果 Please guess a magic number:50 Wrong! Too small! Please guess a magic number:75 Wrong! Too big! Please guess a magic number:62 Wrong! Too big! Please guess a magic number:56 Wrong! Too big! Please guess a magic number:53 Wrong! Too big! Please guess a magic number:51 Right! counter = 6
//L6-8
#include <time.h> #include <stdlib.h> #include <stdio.h> main() { int magic, guess, counter = 0; srand(time(NULL)); magic = rand() % 100 + 1; do{ printf("Please guess a magic number:"); scanf("%d", &guess); counter ++; if (guess > magic) printf("Wrong! Too big! "); else if (guess < magic) printf("Wrong! Too small! "); else printf("Right! "); }while (guess!=magic && counter<10);/*猜不对且未超过10次时继续猜*/ printf("counter = %d ", counter); }
//L6-9
#include <time.h> #include <stdlib.h> #include <stdio.h> main() { int magic, guess, counter = 0; int ret; /* 保存函数scanf()的返回值 */ srand(time(NULL)); magic = rand() % 100 + 1; do{ printf("Please guess a magic number:"); ret = scanf("%d", &guess); while (ret != 1) /* 若存在输入错误,则重新输入 */ { while (getchar() != ' '); /* 清除输入缓冲区中的非法字符 */ printf("Please guess a magic number:"); ret = scanf("%d", &guess); } counter++; if (guess > magic) printf("Wrong!Too big! "); else if (guess < magic) printf("Wrong!Too small! "); else printf("Right! "); } while (guess!=magic && counter<10); /*猜不对且未超过10次时继续猜*/ printf("counter = %d ", counter); }
//L6-10
#include <time.h> #include <stdlib.h> #include <stdio.h> main() { int magic, guess, counter = 0, ret; char reply; /* 保存用户输入的回答 */ srand(time(NULL)); do{ magic = rand() % 100 + 1; do{ printf("Please guess a magic number:"); ret = scanf("%d", &guess); while (ret != 1) /* 若存在输入错误,则重新输入 */ { while (getchar() != ' '); /* 清除输入缓冲区中的非法字符 */ printf("Please guess a magic number:"); ret = scanf("%d", &guess); } counter++; if (guess > magic) printf("Wrong!Too big! "); else if (guess < magic) printf("Wrong!Too small! "); else printf("Right! "); } while (guess!=magic && counter<10); /*猜不对且未超10次继续猜*/ printf("counter = %d ", counter); printf("Do you want to continue(Y/N or y/n)?"); /*提示是否继续*/ scanf(" %c", &reply); /* %c前有一个空格 */ }while (reply=='Y' || reply=='y'); /* 输入Y或y则程序继续 */ }
goto、break、continue和return语句是C语言中用于控制流程转移的跳转语句
goto语句为无条件转向语句,它既可以向下跳转,也可往回跳转
它的作用是在不需要任何条件的情况下直接使程序跳转到该语句标号所标识的语句去执行
其中语句标号代表goto语句转向的目标位置,应使用合法的标识符表示语句标号,其命名规则与变量名相同
break语句用于退出switch结构,也可用于由while、do-while和for构成的循环体中
当执行循环体遇到break语句时,循环将立即终止,从循环语句后的第一条语句开始继续执行
//L6-11
#include <stdio.h> main() { int i, n; for (i=1; i<=5; i++) { printf("Please enter n:"); scanf("%d", &n); if (n < 0) goto END; printf("n = %d ", n); } END:printf("Program is over! "); }
continue语句与break语句都可用于对循环进行内部控制,但二者对流程的控制效果是不同的
当在循环体中遇到continue语句时,程序将跳过continue语句后面尚未执行的语句,开始下一次循环
即只结束本次循环的执行,并不终止整个循环的执行
//L6-12
#include <stdio.h> main() { int i, n; for (i=1; i<=5; i++) { printf("Please enter n:"); scanf("%d", &n); if (n < 0) continue; printf("n = %d ", n); } printf("Program is over! "); }
在嵌套循环的情况下,break语句和continue语句只对包含它们的最内层的循环语句起作用,不能用break语句跳出多重循环。
若要跳出多重循环,break语句只能一层一层地跳出,显然goto语句是跳出多重循环的一条捷径。
以下两种情况使用goto语句可以提高程序的执行效率,使程序结构更清晰
- 快速跳出多重循环
- 跳出共同的出口位置,进行退出前的错误处理工作
//L6-13
#include <stdio.h> main() { int x; int find = 0; /* 置找到标志变量为假 */ for (x=1; !find; x++) /* find为假时继续循环 */ { if (x%5==1 && x%6==5 && x%7==4 && x%11==10) { printf("x = %d ", x); find = 1; /* 置找到标志变量为真 */ } } }
//运行结果 x = 2111
结构化程序设计:
结构化程序设计是一种进行程序设计的原则和方法,按照这种原则和方法设计的程序,具有结构清晰、容易阅读、容易修改、容易验证等特点
- 程序应该只有一个入口和一个出口
- 不应有不可达语句和死循环
- 尽量避免使用goto语句,因为它破坏了结构化设计风格
- 采用自顶向下、逐步求精的模块化设计方法
常用的程序调试与排错方法:
- 除为了取得堆栈轨迹和一两个变量的值之外,尽量不要使用排错系统,因为人很容易在复杂数据结构和控制流的细节中迷失方向
- 有时以单步运行遍历程序的方式还不如努力思考,并辅之以在关键位置增设打印语句和测试代码,后者的效率更高
排错策略:
- 缩减输入数据,设法找到导致失败的最小输入
- 注释掉一些代码,分而治之
- 增量测试
类型溢出
//L6-14
#include <stdio.h> main() { long i, sum = 0; for (i=1; ;i++) { sum = sum + i*i*i; if (sum >= 1000000) break; } printf("count = %d ", i); }
//运行结果 count = 45
//L6-15
#include <stdio.h> main() { double term, result = 1; int n; for (n=2; n<=100; n=n+2) { term = (double)(n * n) / ((double)( n - 1) * ( n + 1)); result = result * term; } printf("pi = %f ", 2 * result); }
//运行结果 pi = 3.126079