f = p * w * s * (1 d);
printf("freight = %15.4f\n", f);
}
注意:c、s 是整型变量,因此 c s / 250 为整数。当 s≥3000时,令 c 12 ,而不使 c 随 s 增大,这是为了在 switch 语句中便于处理,用一个 case 就可以处理所有 s≥3000的情况。
随堂练习
编程:从键盘上输入一百分制成绩,要求输出成绩相应的等级 A、B、C、D 和 E。其中 90 分以上为 A,80~89 分为 B,70~79 分为 C,60~69 分为 D,60 分以下为 E。
3.5 循环结构程序设计
如果要计算“1+2+3+…+100”的值,根据现有的知识,只能一项一项地求和,这是一件非常 烦琐的事情,如果数据更大点,就更加烦琐,甚至根本无法完成。要解决这类问题,就需要用到 循环。循环结构是结构化程序设计的基本结构之一,它和顺序结构、选择结构共同作为各种复杂 程序的基本构造单元。C 语言中提供的循环语句有 while、do…while 以及 for 语句。
3.5.1 从 while 语句学自增和自减运算符
程序 3_9.c 使用 while 循环来求“1+2+3+…+100”的值。
程序清单 3.9 3_9.c 程序
#include <stdio.h>
void main() {
int i = 1, sum = 0;
while(i <= 100) {
sum += i;
i++;
}
sum 的初始值一定 要赋 0 值
while 循 环 语 句 , 循 环 体 为
“sum+=i;i++;”。其中“i++”
等价于“i= i+1”
printf("1+ 2 + 3 + … + 100 = %d\n", sum);
}
程序 3_9.c 的流程图如图 3.16 所示,运行结果如图 3.17 所示。
图 3.16 程序 3_9.c 的流程图 图 3.17 程序 3_9.c 的运行结果
while 语句用来实现“当型”循环结构,其一般形式如下:
while(表达式) {
语句组;
}
当语句组只有一条语句的时候可以省略大括号。在语句组中必须包含能改变表达式的值 的语句来使表达式的值最终变为假,否则循环永远不会中止。例如:
index = 1;
while(index < 5) {
printf("Hello,C Program!\n");
}
上面的代码段就会陷入“死循环”,程序会无休止地输出“Hello,C Program!”。因为变 量 index 的值为 1,如果 index 的值不发生变化,index < 5 这个条件将永远成立,while 语句 也就会一直循环下去。所以在循环体中应该添加能够使循环正常结束的语句。如程序 3_10.c 所示。
程序清单 3.10 3_10.c 程序
#include <stdio.h>
void main() {
int index = 1;
while(index < 5) {
printf("index = %d\n", index);
index++;
printf("after index++ index = %d\n", index);
}
printf("The loop has finished.\n");
}
每 次 循 环 的 时 候 , 执 行 index++,index 增加 1,当 index<5 不成立的时候就会 退出循环
i = 1
i ≤ 100 真 假
sum += i i++
程序 3_10.c 的运行结果如图 3.18 所示。从图 3.18 可以看出在第 4 次循环中,执行完 index++
后,index 的值已经是 5,然而循环此时并不结束,继续执行下面的输出语句 printf("after index++
index = %d\n", index)。执行完输出语句后,再去判断表达式 index < 5 是否成立,此时发现该 表达式为假,才结束 while 循环,执行 while 后面的语句。
图 3.18 程序 3_10.c 的运行结果
如果 while 语句的表达式刚开始就不成立,如将程序清单 3.10 中的 index 的初始值改成 10,
这时,程序永远不会执行循环体,因为条件 index < 5 一开始就为假。
1.自增、自减运算符
在程序 3_10.c 中使用了自增运算符“++”,其作用是使变量的值增 1。与之对应的还有自 减运算符“”,其作用是使变量的值减 1。
自增、自减运算符的使用有两种方式:
前缀模式:++变量,变量(在使用变量之前,先使变量的值加(减)1)。
后缀模式:变量++,变量(在使用变量之后,使变量的值加(减)1)。
从表上看,“++变量”和“变量++”的作用都是使变量的值增 1,但是它们还是有区别的。
“++变量”是先让变量增 1 后,再使用变量的值;而“变量++”则是先使用变量的值,变量 再增 1。自减运算符“”的原理一样。
程序清单 3.11 3_11.c 程序
#include <stdio.h>
void main() {
int a = 1, b = 1;
int aplus, plusb;
aplus = a++;
plusb = ++b;
printf("a = %d\naplus = %d\nb = %d\nplusb = %d\n", a, aplus, b, plusb);
}
程序 3_11.c 的运行结果如图 3.19 所示。从图 3.19 可以看出,执行“aplus = a++”是先将 a 的值 1 复制给 aplus,aplus 的值为 1,然后 a 的值再增 1 变成 2。而“plusb = ++b”则是先使 b 的值增 1 变成 2,然后再将 b 的值赋值给 plusb,plusb 的值为 2。
循环体被执行了 4 次,循环结束 时,index 值为 5
图 3.19 程序 3_11.c 的运行结果
自增和自减运算符有很高的优先级,常用的运算符只有圆括号比它们的优先级高。其结 合性是“自右向左”。所以“x * y++”代表“x * (y++)”而不是“(x * y)++”。而后者也不是一 个合法的表达式,自增、自减运算符只能用于变量,而不能用于常量或表达式。“5++”或“(x
* y)++”都是不合法的。
2.含有自增、自减运算符的表达式的不确定性
C 语言提供的运算符和表达式使用非常灵活,这种灵活性往往也会带来麻烦。由于 ANSI C 并未规定表达式中的子表达式的求解顺序,由各个编译系统自己决定,则表达式:
a = fun1() + fun2()
并非所有的编译系统都是先求解 fun1(),然后再求解 fun2()。在一般的情况下,可能先求 解 fun1()或者先求解 fun2()的结果是一样的,但是在很多时候也是不一样的,如:
n = 2;
a = fun1(n) + fun2(n++);
如果先求解函数 fun1,则 a = fun1(2) + fun2(2),如果先求解 fun2,则 a = fun1(3)+fun2(2)。
又如:
n = 2;
y = n++ + n++;
当该语句被执行后,n 的值变成 4,但是 y 的值是不确定的。有的编译器可能将 n = 2 作 为表达式中所有 n 的值,从而求出 y 为 4。但也有的编译器按照自左向右的顺序来求解表达式,
先求解第一个 n++,这个时候 n++先使用 n 的值 2,然后 n 再增 1,变成 3。在求解第二个 n++
的时候,同理,先使用 n 的值 3,然后 n 再变成 4。此时 y 值为 5。
对于这类问题,在 C 语言中还有一些,例如:
n = 2;
printf("%d, %d\n",n,n++);
在调用函数时,C 语言并未规定对实参的求解顺序,对于上例,有的系统从左到右求解,
输出“2, 2”,而多数系统则是从右向左进行求解,输出“3, 2”。
对于上述的一些不确定性,我们在写程序的时候一定要避免,不要写出别人看不懂,也 不知道系统会怎样执行的代码。在使用自增或自减运算符的时候,要遵循以下两个原则:
(1)如果一个变量出现在同一个函数的多个参数中时,不要用自增或自减运算符。
(2)如果一个变量多次出现在一个表达式里时,不要用自增或自减运算符。
3.5.2 do…while 语句
下面使用 do…while 语句来改写程序 3_9.c(求 1+2+3+…+100 的值)。程序清单如下:
程序清单 3.12 3_12.c 程序
#include <stdio.h>
void main() {
int i = 1, sum = 0;
do {
sum += i;
i++;
}while(i <= 100);
printf("1 + 2 + 3 + ... + 100 = %d\n", sum);
}
程序的运行结果与图 3.17 相同。其流程图如图 3.20 所示。从图 3.20 可以看出,程序先执 行循环体语句,然后再判断 while 后面的条件是否成立。当表达式为真,则返回重新执行循环 体语句,如此反复,直到 while 后面的表达式为假才结束循环。
图 3.20 程序 3_12.c 的流程图 可以看到 do…while 语句的一般形式如下:
do {
循环体语句组;
}while(表达式);
可以看到,对于同一个问题,可以使用 while 语句,也可以使用 do…while 语句,二者可 以互相替换。但是它们还是有区别的,while 语句先判断条件是否成立,然后根据条件的结果 来决定是否执行循环体,而 do…while 语句先执行循环体一次,然后再判断表达式成立与否。
即 do…while 语句至少要执行循环体一次。例如:
程序清单 3.13 3_13.c 程序
/*程序 A*/ /*程序 B*/
#include <stdio.h> #include <stdio.h>
void main() void main() 分号“;”不能少
注意分号“;”不能少
i ≤ 100 真 假
sum = 0 i = 1
sum += i i++
{ {
int sum = 0, i; int sum = 0, i;
scanf("%d", &i); scanf("%d", &i);
while(i <= 100) do { {
sum += i; sum += i;
i++; i++;
} }while(i <= 100);
printf("sum = %d\n", sum); printf("sum = %d\n", sum);
} }
程序的运行结果如图 3.21 所示。可以看出,当输入的 i 值都满足 i≤100的时候,程序 A 和程序 B 的运行结果相同。而当输入的 i 值不满足 i≤100的时候,二者的结果就不同了,因 为此时对于 while 语句来说,循环体一次也不会被执行,而 do…while 语句则会执行循环体 一次。
(a)程序 A 的运行结构 (b)程序 B 的运行结构
图 3.21 程序 3_13.c 的运行结果
3.5.3 灵活强大的循环语句——for 语句
C 语言中的 for 语句是使用最为灵活的语句。同样,先使用 for 语句来改写程序 3_9.c 求 1+2+3+…+100 的值。程序清单如下:
程序清单 3.14 3_14.c 程序
#include <stdio.h>
void main() {
int i, sum = 0;
for(i = 0; i <= 100; i++) {
sum += i;
}
printf("1 + 2 + 3 + ... + 100 = %d\n", sum);
}
程序的运行结果与图 3.17 相同。可以看出,for 语句的一般形式如下:
for(表达式 1;表达式 2;表达式 3) {
循环体语句组;
}
for 语句,注意括号中的三个 表达式是用“;”分隔的
循环体语句 i = 1 的结果
i = 101 的结果
i = 1 的结果
i = 101 的结果
当循环体语句组只有一条语句的时候,大括号可以省略。建议初学者不要省略,以避免 出错。for 语句的流程如图 3.22 所示。从图中可以看出,其执行过程如下:
(1)求解表达式 1;
(2)求解表达式 2,如果为真,则执行循环体语句组,执行结束后转步骤(3);如果表 达式的结果为假,则结束循环,转步骤(5);
(3)求解表达式 3;
(4)转步骤(2);
(5)循环结束,执行循环语句之后的其他语句。
图 3.22 for 语句执行流程
for 语句的 3 个表达式均可省略,但是里面的分号“;”不能省略。需要注意的是,省略相 应的表达式,就需要在其他地方弥补相应表达式的功能,以保证程序的正常运行。
3.5.4 逗号运算符和逗号表达式
逗号运算符扩展了 for 循环语句的灵活性,它可以使 for 循环语句使用多个初始化或循环 变量更新表达式。例如:
程序清单 3.15 3_15.c 程序
#include <stdio.h>
void main() {
int i, sum;
for(i = 1, sum = 0; i <= 100; sum += i,i++);
printf("sum = %d\n", sum);
}
程序 3_15.c 也是求 1+2+3+…+100 的值,其运行结果如图 3.23 所示。可以看到,在 for 语句中使用了逗号表达式,使得 for 语句非常简练。
有“;”,表示 for 语句没有 循环体语句。
逗号表达式 真
假 求解表达式 1
表达式 2
循环体语句组
求解表达式 3
循环语句的下 一语句
图 3.23 程序 3_15.c 的运行结果
从程序 3_15.c 可以看出,用逗号运算符可以将两个或两个以上的表达式连接起来,称为 逗号表达式,其一般形式如下:
表达式 1,表达式 2,…,表达式 n
逗号表达式的求解过程为:按照先后顺序,依次求解表达式 1,表达式 2,……,最后求 解表达式 n。并将最后求解的表达式 n 的值作为整个逗号表达式的值。如程序 3_15.c 中的:
i = 1, sum = 0;
先求解赋值表达式 i = 1,然后再求解表达式 sum = 0,并将表达式 sum = 0 的值作为整个 逗号表达式的值,所以该逗号表达式的值为 0。
从附录三可知,逗号运算符是所有运算符中级别最低的,所以“a = 3 * 5, a * 2”应理解成
“(a = 3 * 5), (a * 2)”而不是“a = (3 * 5, a * 2)”。在逗号表达式的子表达式可以是任意的表达 式,包括逗号表达式。如(a = 3 * 5, a * 2), a4。
逗号运算符并不只限于在 for 语句中使用。同时,需要引起注意的是,在定义时变量之间 的分隔符,以及在 printf()函数和 scanf()函数中一些分隔符不属于逗号运算符。如下面语句中 的“,”都不是逗号运算符。
int i, j, k;
printf("%d,%d,%d\n", i, j, k);
小提示:几种循环语句的比较
(1)while 语句、do…while 语句和 for 语句都可以用来处理同一问题,很多时候,它们 之间可以互换。
(2)在 while 和 do…while 语句中,只是在 while 后的括号里指定了循环条件,应该在循 环体内包含使循环能够正常结束的语句,避免出现死循环。而 for 语句在表达式 3 中含有使循 环趋于结束的操作,甚至可以将循环体中的操作全部放到表达式 3 中,因此 for 语句使用更为 灵活,功能更为强大。
(3)用 while 和 do…while 语句循环时,循环变量的赋初值应该在 while 和 do…while 语
(3)用 while 和 do…while 语句循环时,循环变量的赋初值应该在 while 和 do…while 语