3.8 选择结构程序设计举例
例3.17 求一元二次方程ax2+bx+c=0的解(a≠0)。
1 #include "math.h"
2 #include <stdio.h>
3 void main()
4 {
5 float a,b,c,disc,x1,x2,p,q;
6 scanf("%f,%f,%f", &a, &b, &c);
7 disc=b*b-4*a*c;
8 if(fabs(disc)<=1e-6) /*fabs():求绝对值库函数*/
9 printf("x1=x2=%7.2f\n", -b/(2*a));/*输出两个相等的实根*/
10 else
11 {
12 if(disc>1e-6)
13 {
14 x1=(-b+sqrt(disc))/(2*a);/*求出两个不相等的实根*/
15 x2=(-b-sqrt(disc))/(2*a);
16 printf("x1=%7.2f,x2=%7.2f\n", x1, x2);
17 }
18 else
19 {
20 p=-b/(2*a); /*求出两个共轭复根*/
21 q=sqrt(fabs(disc))/(2*a);
22 printf("x1=%7.2f + %7.2f i\n", p, q);/*输出两个共轭复根*/
23 printf("x2=%7.2f - %7.2f i\n", p, q);
24 }
25 }
26 }
说明:由于实数在计算机中存储时,经常会有一些微小误差,所以本案例判断disc是否为0的方法是:判断disc的绝对值是否小于一个很小的数(例如10-6)。
思考题:如果将系数a、b、c定义成整数,能否直接判断disc是否等于0?
例3.18 已知某公司员工的保底薪水为500,某月所接工程的利润profit(整数)与利润提成的关系如下(计量单位:元):
profit≤1000 没有提成;
1000<profit≤2000 提成10%;
2000<profit≤5000 提成15%;
5000<profit≤10000 提成20%;
10000<profit 提成25%。
算法设计要点:
为使用switch语句,必须将利润profit与提成的关系,转换成某些整数与提成的关系。分析本题可知,提成的变化点都是1000的整数倍(1000、2000、5000、……),如果将利润profit整除1000,则当:
profit≤1000 对应0、1
1000<profit≤2000 对应1、2
2000<profit≤5000 对应2、3、4、5
5000<profit≤10000 对应5、6、7、8、9、10
10000<profit 对应10、11、12、……
为解决相邻两个区间的重叠问题,最简单的方法就是:利润profit先减1(最小增量),然后再整除1000即可:
profit≤1000 对应0
1000<profit≤2000 对应1
2000<profit≤5000 对应2、3、4
5000<profit≤10000 对应5、6、7、8、9
10000<profit 对应10、11、12、……
1 #include <stdio.h>
2 void main()
3 {
4 long profit;
5 int grade;
6 float salary=500;
7 printf("Input profit: ");
8 scanf("%ld", &profit);
9 grade= (profit - 1) / 1000;
10 switch(grade)
11 {
12 case 0: break;/*profit≤1000 */
13 case 1: salary += profit*0.1; break; /*1000<profit≤2000 */
14 case 2:
15 case 3:
16 case 4: salary += profit*0.15; break; /*2000<profit≤5000 */
17 case 5:
18 case 6:
19 case 7:
20 case 8:
21 case 9: salary += profit*0.2; break; /*5000<profit≤10000 */
22 default: salary += profit*0.25; /*10000<profit */
23 }
24 printf("salary=%.2f\n", salary);
25 }
3.9 循环语句概述
我们知道目前计算机是不能进行创造性劳动的,但它却是重复性劳动的高手,而重复性劳动常常可以通过循环来实现。
求1~100的累计和。根据已有的知识,可以用“1+2+……+100”来求解,但显然很繁琐。现在换个思路来考虑:
首先设置一个变量sum,其初值为0,用来存放计算之和。利用sum += n来计算(n依次取1、2、……、100),只要解决以下3个问题即可:
(1)将n的初值置为1;
(2)每执行1次“sum += n”后,n增1;
(3)当n增到101时,停止计算。此时,sum的值就是1~100的累计和。
我们可以用if语句+goto语句(无条件转向语句)来实现上述计算。
goto语句的一般格式:
功能为:使系统转向标号所在的语句行执行。
语句标号用标识符表示,它的命名规则与变量名的命名规则相同。例如:
goto label_1; /*合法*/
goto 123; /*不合法*/
用goto语句和if语句构成循环。使用goto语句实现求解1~100累计和的程序如下:(流程如图3.10所示)

图3.10 图3.11
void main()
{
int n=1,sum=0;
loop: sum+=n;n++;
if(n<=100) goto loop;
printf(“sum=%d\n”, sum);
}
注意:结构化程序设计方法,主张限制使用goto语句。因为滥用goto语句,将会导致程序结构无规律、可读性差。
由于需要经常使用这种重复计算结构(称为循环结构),C语言提供了3条循环语句来实现,以简化、并规范循环结构程序设计:
(1)for语句。(2)while语句。(3)do-while语句。
3.10 for语句、while语句、do-while语句
3.10.1 for语句
在3条循环语句中,for语句最为灵活,不仅可用于循环次数已经确定的情况,也可用于循环次数虽不确定,但给出了循环继续条件的情况。
例3.19 求1~100的累计和。
1 #include <stdio.h>
2 void main()
3 {
4 int i,sum=0; /*将累加器sum初始化为0*/
5 for(i=1; i<=100; i++)
6 sum += i; /*实现累加*/
7 printf("sum=%d\n",sum);
8 }
程序运行结果:sum=5050
例3.20 求n的阶乘n!(n!=1*2*……*n)。
1 #include <stdio.h>
2 void main()
3 {
4 int i, n;
5 long fact=1; /*将累乘器fact初始化为1*/
6 printf("Input n: ");
7 scanf("%d", &n);
8 for(i=1; i<=n; i++)
9 fact*=i; /*实现累乘*/
10 printf("%d ! = %ld\n", n, fact);
11 }
程序运行情况如下:Input n: 5↙
5 ! = 120
1. for语句的一般格式
2.for语句的执行过程
执行过程流程如图3.11所示。
(1)求解“循环变量赋初值”表达式。
(2)求解“循环继续条件”表达式。如果其值非0,执行(3);否则,执行(4)。
(3)执行循环体语句组,并求解“循环变量改变”表达式,然后转向(2)。
(4)执行for语句的下一条语句。
3.说明
(1)“循环变量赋初值”、“循环继续条件”和“循环变量改变”部分均可缺省,甚至全部缺省,但其间的分号不能省略。(for( ; ; ))
① 循环变量赋初值省略
例: i=1;
for( ;i<=100;i++)
sun+=i;
由此可以看出循环变量赋初值并不是真的省略了,而是换了一个地方。
② 循环继续条件省略
例: for(i=1; ;i++)
sun+=i;
如果循环条件省略,就不判断循环条件,即认为循环条件始终为真,循环无终止的进行,如果没有别的办法退出循环,将成为死循环。
流程如图3.12所示
图3.12 图3.13
③ 循环变量改变也可以省略,但程序应另外设法保证循环能正常结束。例:
for(i=1;i<=100;)
{
sum+=i;
i++;
}
从这个意义来看,循环变量改变并没有省,而是换了一个地方。如果不能设法结束循环,循环将成为死循环,死循环一般是没有意义的,应当尽量避免。
④ 可以省略循环变量赋初值和循环变量改变这两个表达式,只留下循环继续条件。这里所说的省略要理解成,在一般情况(不是绝对的)下在适当的位置要给循环变量赋初值,要有改变循环变量值的语句。
例: i=1;
for(;i<=100;)
{
sum+=i;
i++;
}
(2)当循环体语句组仅由一条语句构成时,可以不使用复合语句形式。
(3)“循环变量赋初值”表达式,既可以是给循环变量赋初值的赋值表达式,也可以是与此无关的其它表达式(如逗号表达式)。
例如,for(sum=0,i=1;i<=100;i++)
sum += i;
(4)“循环继续条件”部分是一个逻辑量,除一般的关系(或逻辑)表达式外,也允许是数值(或字符)表达式。C语言将非0值看成是逻辑真,将0值看成是逻辑假。
3.10.2 while语句
1. while语句又叫“当型”循环语句,它的一般格式为:
2. while语句的执行过程
执行过程流程如图3.13所示。
(1)求解“循环继续条件”表达式。如果其值为非0,转(2);否则转(3)。
(2)执行循环体语句组,然后转(1)。
(3)执行while语句的下一条。
显然,while循环是for循环的一种简化形式(缺省“变量赋初值”和“循环变量改变”表达式)。
while循环的特点是,先判断条件后执行循环体。
例3.21 用while语句求1~100的累计和。
1 #include <stdio.h>
2 void main()
3 {
4 int i=1,sum=0; /*初始化循环控制变量i和累计器sum*/
5 while( i<=100 )
6 {
7 sum += i; /*实现累加*/
8 i++; /*循环控制变量i增1*/
9 }
10 printf("sum=%d\n",sum);
11 }
程序运行情况如下:
sum=5050
3.10.3 do-while语句
1.do-while语句又叫“直到”循环语句,它的一般格式为:
尾行的分号不能省!!。
当循环体语句组仅由一条语句构成时,可以不使用复合语句形式。
2.执行过程
执行流程如图3.14所示。
(1)执行循环体语句组。

图3.14
(2)计算“循环继续条件”表达式。如果“循环继续条件”表达式的值为非 0(真),则转向(1)继续执行;否则,转向(3)。
(3)执行do-while的下一条语句。
do-while循环语句的特点是:先执行循环体语句组,然后再判断循环条件。
例3.22 用do-while语句求解1~100的累计和。
1 #include <stdio.h>
2 void main()
3 {
4 int i=1, sum=0; /*定义并初始化循环控制变量,以及累计器*/
5 do
6 {
7 sum += i; /*累加*/
8 i++;
9 }while(i<=100); /*循环继续条件:i<=100*/
10 printf("sum=%d\n",sum);
11 }
程序执行结果为:sum=5050
do-while语句比较适用于处理:不论条件是否成立,先执行1次循环体语句组的情况。除此之外,do-while语句能实现的,for语句也能实现,而且更简洁。
如果例3.20和例3.21循环条件一开始就为假,它们的运算结果如何,同学们可以试一试。
3.10.4 循环的嵌套
如果循环体内,又包含另一个完整的循环结构,称为循环的嵌套。循环可以允许多层嵌套。
for语句、while语句、do-while语句都允许进行嵌套,并且可以进行相互嵌套。
(1)for循环嵌套 如图3.15~图3.17
(2)while循环嵌套 如图3.18~图3.20
(3)do-while循环嵌套 如图3.21~图3.23
关于循环的几点注意:

图3.15 图3.16
图3.17
图3.18 图3.19
图3.20
(3)do-while循环嵌套 如图3.18~图3.20
图3.21 图3.22
图3.23
(1)四种循环都可以处理同一问题,没有孰优孰劣之分。但一般不用goto语句来实现循环。
(2)while和do-while循环,只在while后面指定循环条件,因此在循环体中要有使循环趋于结束的语句。
(3)一般情况下我们不能忘记“循环三要素”,即“循环变量赋初值”、“循环结束条件(或继续条件)”、“循环变量的改变”(可增可减)。
3.11 break语句与continue语句
为了使循环控制更加灵活,C语言提供了break语句和continue语句。
1. 一般格式:
2.功能
我们在前面已经知道break语句可以跳出switch结构,实际上break语句还可以用于循环语句中。
(1)break:强行结束循环,转向执行循环语句的下一条语句。
(2)continue:结束本次循环。对于for循环,跳过循环体其余语句,转向循环变量改变表达式的计算;对于while和do-while循环,跳过循环体其余语句,但转向循环继续条件的判定。
(3)break和continue语句对循环控制的影响如图3.24和图3.25所示。
3.说明
(1)break能用于循环语句和switch语句中,continue只能用于循环语句中。
(2)循环嵌套时,break和continue只影响包含它们的最内层循环,与外层循环无关。
例3.23 把100~200之间的不能被3整除的数输出,并且每行输出10个。
1 #include <stdio.h>
2 void main()
3 {
4 int n,count=0;
5 for(n=100;n<=200;n++)
6 {
7 if(n%3==0)
8 continue;
9 count++;
10 printf("%5d",n);
11 if(count%10==0)
12 printf("\n");
13 }
14 }
图3.24 图3.25
3.12 循环结构程序举例
例3.24 求Fibonacci数列的前40个数。
该数列的生成方法为:F1=1,F2=1,Fn=Fn-1+Fn-2(n>=3),即从第3个数开始,每个数等于前2个数之和。
1 #include <stdio.h>
2 void main()
3 {
4 long f1=1,f2=1; /*定义并初始化数列的头2个数*/
5 int i=1; /*定义并初始化循环控制变量i*/
6 for( ; i<=20; i++ ) /*1组2个,20组40个数*/
7 {
8 printf("%15ld%15ld", f1, f2); /*输出当前的2个数*/
9 if(i%2==0)
10 printf("\n"); /*输出2次(4个数),换行*/
11 f1 += f2; /*计算下2个数*/
12 f2 += f1;
13 }
14 }
例3.25 输出10~100之间的全部素数。所谓素数n是指,除1和n之外,不能被2~(n-1)之间的任何整数整除。
算法设计要点:
(1)显然,只要设计出判断某数n是否是素数的算法,外面再套一个for循环即可。
(2)判断某数n是否素数的算法:根据素数的定义,用2~(n-1)之间的每一个数去整除n,如果都不能被整除,则表示该数是一个素数。
判断一个数是否能被另一个数整除,可通过判断它们整除的余数是否为0来实现。
源程序如下:
1 #include <stdio.h>
2 void main()
3 {
4 int i=11, j, counter=0;
5 for(;i<=100;i+=2) /*外循环:为内循环提供一个整数i*/
6 {
7 for(j=2;j<=i-1;j++)/*内循环:判断整数i是否是素数*/
8 if(i%j==0) /*i不是素数*/
9 break; /*强行结束内循环,执行下面的if语句*/
10 if(j>=i) /*整数i是素数:输出,计数器加1*/
11 {
12 printf("%6d",i);
13 counter++;
14 }
15 if(counter%10==0)/*每输出10个数换一行*/
16 printf("\n");
17 }
18 printf("\n");
19 }
3.13 良好的源程序书写习惯
顺序程序段中的所有语句(包括说明语句),一律与本顺序程序段的首行左对齐,并且采用缩进对齐的方式,使程序便于阅读。
|