这一讲开始之前,我们需要准备一个函数。这个函数的作用就是把一个百分制成绩转换成等级制。转换的过程我已经在讲解分支结构的时候详细阐述了,现在仅提供函数如下:
/* 准备 grade.h */
char grade(int score) {
switch (score / 10) {
case 10: case 9:
return 'A';
case 8:
return 'B';
case 7:
return 'C';
case 6:
return 'D';
case 5: case 4: case 3:
case 2: case 1: case 0:
return 'E';
default:
return 0;
}
}
在讲解分支结构时提出的实例都只能解决一个成绩的情况。假如需要将 5 个人的成绩进行转换,最简单的办法如下:
/* 例1 */
#include "grade.h"
#include
void main() {
int n;
printf("请输入成绩: ");
scanf("%d", &n);
printf("等级为: %c", grade(n));
printf("请输入成绩: ");
scanf("%d", &n);
printf("等级为: %c", grade(n));
printf("请输入成绩: ");
scanf("%d", &n);
printf("等级为: %c", grade(n));
printf("请输入成绩: ");
scanf("%d", &n);
printf("等级为: %c", grade(n));
printf("请输入成绩: ");
scanf("%d", &n);
printf("等级为: %c", grade(n));
}
例1 的结构非常简单易懂,但是书写起来只有一个感觉--烦!同样的三句话,居然重复了 5 次。如果这个班的人数不止 5 人,而是数十人的话,……天啊!简直想都不敢想!
C 语言当然不会笨得来一点方便都不给,这个"方便",就是循环结构。循环结构的目的就是减少重复代码,减轻程序员的负担。而其形式,在 C 语言中有三种:for 循环、while 循环和 do-while 循环。正面分别介绍如下:
for 循环的基本格式是 for (语句1; 逻辑表达式1; 语句2) {语句组1},大括号包括其中的语句组1 也可以是一条语句。其执行过程如下:先执行语句1,然后判断逻辑表达式1。如果逻辑表达式1 的值为"真(非0)",则执行语句组1,否则结束循环。在没结束循环的情况下,执行了语句组1 之后,执行语句2。然后再对逻辑表达式1 进行判断,再……,如此循环直到逻辑表达式1 为"假(0)"时为止。于是,例1 可以改写如下:
/* 例2 */
#include "grade.h"
#include
void main() {
int n, i;
for (i = 0; i < 5; i++) {
printf("请输入成绩: ");
scanf("%d", &n);
printf("等级为: %c", grade(n));
}
}
嘿,这么简洁?!知道循环结构的便利之处了吧。例2 中 i 从 0 到 4,共执行了 5 次循环体。当然,也可以跟据个人的习惯,将例2 中的 for 语句改写为:
for (i = 1; i <= 5; i++) {...}
这样或许更容易理解,但我不推荐这样做,因为 C 语言中的数组下标是以 0 开始的,采用例1 中的 for 语句更容易在循环体中读写数组元素。就上例,如果我们要把所有成绩输入完成之后再逐一将其等级打印出来,就需要用到数组:
/* 例3 */
#include "grade.h"
#include
#define N 5
void main() {
int n[N], i;
for (i = 0; i < N; i++) {
printf("请输入成绩: ");
scanf("%d", &n[i]);
}
for (i = 0; i < N; i++) {
printf("第 %d 个成绩的等级为: %c", i, grade(n[i]));
}
}
例3 中,如果 i 从 1 到 N 循环的话,读写数组元素时就应该使用 n[i - 1] 而不是 n[i]。那么每执行一次循环体就会多一次减法运算,在一个循环 N 次的循环中,就会多进行 N 次减法运算,大大降低了程序效率。
C 语言是一种灵活的语言。它的 for 循环也不是一成不变,必须按照它的基本格式书写。暂时卖个关子,先看看从例3 修改过来的例4:
/* 例4 */
#include "grade.h"
#include
#define N 5
void main() {
int n[N], i = 0;
for (; i < N; ) {
printf("请输入成绩: ");
scanf("%d", &n[i++]);
}
for (i = 0; ; i++) {
if (i == N) {
break;
}
printf("第 %d 个成绩的等级为: %c", i, grade(n[i]));
}
}
是不是很奇怪,例4 中的两个 for 循环中与上面两个例子的 for 循环相比,都少用了一些语句,怎么回事?且听我慢慢道来:
第一个 for 循环中,少了 i = 0 和 i++ 两句。其实仔细一看,这两句也没少。i = 0 已经在给 i 赋初值的时候就实现了,而 i++ 是在 scnaf 语句中顺便实现的。所以,虽然没有 i = 0 一句,但循环开始之前 i 的值已经是 0 了;虽然没有 i++ 一句,但循环体中实际也改变了 i 的值。
第二个 for 循环中少的只是那一句判断。而正是由于少了这一句判断,for 循环便不能自已结束。如果我在循环体中也不采取措施结束循环的话,这个循环将一直不停的执行下去,形成死循环。于是,我在循环体中对 i 进行判断,当它等于 N的时候执行一个 break 语句,跳出循环,填补了 for 语句中没有逻辑表达式的缺陷。
甚至,一个 for 语句就可以写成 for (; ; ) {...},这样一个 for 语句,简单的说,就是一个死循环。
还有一点需要说明,就是 break 语句。这个语句在讲解 switch 分支结构的时候就已经见到过了。它除了能用于 switch 分枝结构之外,还能用于所有的三种循环结构。其作用就是两个字--"跳出"。switch 分枝结构中,它用于跳出整个 switch 语句;而在循环结构中,它自然是用于跳出循环,执行它之后,循环体中所有其它语句都不会再执行,整个循环就此中断。
学会了 for 循环,虽然可以简化不少代码,但它似乎只能解决循环次数固定的情况。如果人数不固定,又该怎么办?比如规定如下:输入若干 0 至 100 的成绩,如果成绩不在此范围,则表式结束。请看例5,例6:
/* 例5 */
#include "grade.h"
#include
void main() {
int n;
printf("请输入成绩: ");
scanf("%d", &n);
while (grade(n) != 0) {
printf("等级为: %c", grade(n));
printf("请输入成绩: ");
scanf("%d", &n);
}
}
/* 例6 */
#include "grade.h"
#include
void main() {
int n;
do {
printf("请输入成绩: ");
scanf("%d", &n);
if (grade(n) != 0) {
printf("等级为: %c", grade(n));
}
} while (grade(n) != 0);
}
例5 和例6 分别使用了 while 循环和 do-while 循环结构。两种循环结构都是在 while 后的逻辑表达式为真时执行循环体,为假时结束循环。二者的不同在于:while 循环是先判断,再执行循环体;而 do-while 循环而是先执行循环体后再作判断。因此,使用 do-while 循环至少要执行一次循环体。至于循环的执行过程,就请读者自己分析了。
例6 中对于同一个 n,至少要执行两次 grade(n),大大降低了程序的效率。虽然可以用一个 char 型变量来解决这个问题,但我不想这样作。于是又有如下方式的改写:
/* 例7 */
#include "grade.h"
#include
void main() {
int n;
do {
printf("请输入成绩: ");
scanf("%d", &n);
if (grade(n) == 0) {
break;
}
printf("等级为: %c", grade(n));
} while (1);
}
例7 中 while 语句的逻辑表达示值始终是真(1),所以这个循环也就是一个死循环,必须在循环体中用 break 语句跳出。用一个 if 语句判断跳出的时机,这也是很好理解的,就不多说了。但是看看下面的例8,可能就不是那么明白了。
/* 例8 */
void main() {
int n;
do {
printf("请输入成绩: ");
scanf("%d", &n);
if (grade(n) == 0) {
continue;
}
printf("等级为: %c", grade(n));
} while (grade(n) != 0);
}
例8 的和例6 的区别主要就在于一个 continue 语句。这个语句可是循环结构所特有的,其作用与 break 语句相比,即有相似之处,也有相对之处。执行了 continue 语句之后,循环体中 continue 以下的语句将不再在这次循环中执行,但循环并不中断,而是开始下一次循环。
如例8 中,满足 grade(n) == 0 的条件后,执行 continue 语句。然后 printf 语句被跳过,直接到 while 语句判断是否结束循环,如果不满足结束循环的条件,那么将会要求输入下一个成绩。不过例8 中,只要能执行到 continue 语句,while 后的条件就一定为假,循环结束。例8 和例7 的区别就在于,例8 是正常结束,而例7 是被中断。
break 和 continue 两个语句在 C 语言的循环结构中有着举足轻重的作用。虽然使用巧妙的结构化程序手段可以避免使用这两个语句,但这两个语句的确能使程序结构更加简单明了。所以,只要能用上它们的地方,我极力推荐使用它们。
三种循环结构都说完了,但我还要强调 C 语言的灵活性。虽然循环结构有三种形式,但只使用其中仍意一种都能达到目的。例5 和例6 就展示了使用 while 和 do-while 两种形式完成同要的任务。至于 for 循环,前面说它"似乎只能解决循环次数固定的情况",这"似乎"二字并非笔误。它乍看只能解决循环次数固定的情况,但仔细一想,它也的确能解决循环次数不固定的情况。如例5 例6 用 for 循环的形式分别可以改写为:
/* 例9 */
#include "grade.h"
#include
void main() {
int n;
printf("请输入成绩: ");
scanf("%d", &n);
for (; grade(n) != 0; ) {
printf("等级为: %c", grade(n));
printf("请输入成绩: ");
scanf("%d", &n);
}
}
/* 例10 */
#include "grade.h"
#include
void main() {
int n;
int b = 1;
for (; b; ) {
printf("请输入成绩: ");
scanf("%d", &n);
if (grade(n) != 0) {
printf("等级为: %c", grade(n));
} else {
b = 0;
}
}
}
看看例9 和例5,例10 和例6,是不是有异曲同工之妙?其实 C 语言的循环结构远远不止这些变形,只要你细心思考,一定还会发现更多"变形妙用"。哈哈哈哈,一吐为快,心中舒服多了。
本文地址:http://com.8s8s.com/it/it30143.htm