资源描述
第三章 循环结构
在程序设计中,如果需要重复执行的某些操作,就要用到循环结构。使用循环结构编程时,首先要明确2个问题,哪些操作需要重复执行?这些操作在什么情况下重复执行?它们分别对应循环体和循环条件,然后就可以选用C语言提供的3种循环语句: while、do-while 、for实现循环,这写循环语句我们在第一章“1、2 C语言程序设计语法”和第二章“2.6字符类型”中也都见过,而第二章2、4节的用if语句和goto语句构成“直到型(无条件转移)”也叫循环结构。
3.1 while 语句
while是用来实现循环的语句,而且它的适用面很广。其一般格式为:
while (表达式)
循环语句;
其执行的流程如图3.1所示。当表达式的值为“真”时,循环执行,直到表达式的值为“假”,循环终止并继续执行while的下一条语句。
while语句的用法: 假
(1)while语句中的表达式可以是任意合法的表达式, 表达式
循环体语句只能是一条语句。 真
(2)while语句的构成简单,只有一个表达式和一条 循环体语句
循环体语句,分别对应循环的两个核心要素:循
环条件和循环体,可以直接把循环问题的分析设
计转换为语句实现。 While的下一条语句
(3)循环的实现一般包括4部分,即初始化、条件控 图3.1 while语句的执行流程
制、重复的操作以及通过改变循环变量的值最终
改变条件的真假性,使循环能正常结束。当使用while语句时,由于它只有2个
成分(表达式和循环体语句),就需要另加初始化部分,第4部分while的循环体
语句中必须包含能最终改变循环条件真假性的操作。
例3.1 从键盘输入一批学生的成绩,计算平均分,并统计不及格学生的人数。
这是一个累加求和的问题,将输入的成绩先累加,最后再除以学生的数量,算出平均分。本题的难点在于确定循环条件,由于题目中没有给出学生的数量,不知道输入数据的个数,所以无法事先确定循环次数,这时需要自己设计循环条件,可以用一个特殊的数据作为正常输入数据的结束标志,由于成绩都是正数,就选用一个负数作为结束标志,因此,循环条件就是输入的数据grade>=0 。
#include <stdio.h>
int main()
{ int num=0,fai=0; //num记录输入数据的个数,以便统计平均分;fai记录不及格人数
double grade , total=0; //grade存放输入的成绩,total保存成绩之和
printf("Enter grades:\n"); //输入提示
scanf("%lf",&grade); // 输入第一个学生成绩
while( grade >= 0 )
{ total = total + grade; //累加成绩
num ++; // 计数
if(grade<60) fai++; // 不及格人数统计
scanf("%lf",&grade); //再读入一个新数据,为下次循环做准备
}
if(num!=0)
{ printf("Grade verage is %.2f\n",total/num); //total/num是累加成绩/成绩个数=平均分
printf(“不及格学生的人数为(failures):%d\n”,fai);
}
else
printf("Grade average is 0\n");
return 0;
}
例3.2 猴子吃桃问题,猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半多一个。以后每天早上都吃了前一天剩下的一半多一个,到第10天早上想再吃时,见只剩下一个桃子了,求第一天共摘下多少桃子。程序编写如下:
#include <stdio.h> // 第10天是1,因为4的1半是2,多1个是3,第10天是1了
main() // 第9天是(1+1)*2=4 (1+1)*2=4是第9天的4
{ int day=9,x1,x2=1; //x1前1天数,x2后1天数 // 第8天是(4+1)*2=10 第8天10
while(day>0) // 第7天是(10+1)*2=22 第7天22
{//前一天桃子数是后一天桃子数加1后的2倍 // 第6天是(22+1)*2=46 第6天46
x1 = ( x2+1 ) * 2; // 第5天是(46+1)*2=94 第5天94
x2 = x1 ; // 第4天是(94+1)*2=190 第4天190
day--; // 第3天是(190+1)*2=382 第3天 382
} // 第2天是(382+1)*2=766 1534/2-1=766是第2天
printf("total = %d\n",x1); // 第1天是(766+1)*2=1534 (766+1)*2=1534是第1天
return 0;
}
运行结果:total = 1534 (也就是猴子第一天摘的桃子是1534个)
3.2 do – while 语句
do – while语句与while语句略有不同,它先执行循环体,后判断循环条件。所以无论循环条件的值如何,至少会执行一次循环体,其一般形式如下:
do
{
循环体语句
}
While( 表达式 );
do – while语句的执行流程如图3.2所示,第一
次进入循环时,首先执行循环体语句,然后再检查循环
控制条件,即计算表达式,若值为“真”,继续循环,直 循环体语句
到表达式的值为“假”,循环结束,执行do-while的下 假
一条语句。 表达式
do-while语句的使用方法和while语句类似,语句 真
中的表达式可以是任意合法的表达式,循环体语句只能
是一条语句;使用时要另加初始化部分,循环体语句必须
包含能最终改变条件真假的操作,例如例3.2中的day>0 do-while的下一条语句
用 - -day来控制,到day减到不大于0时就结束了。 图3.2 do-while语句流程图
do – while语句适用于先循环,后判断循环条件
的情况,一般在循环体的执行过程中明确循环控制条件,它每执行一次循环体后,再判断条件,以决定是否进行下一次循环。
例3.3 求 sn = a + aa + aaa + … + aa…a 之值,其中a是一个数字,例如:
3 + 33 + 333 + 3333 + 33333(此时n是5),a和n由键盘输入。
#include <stdio.h>
main()
{ //a是位上的数如3,n是多少位,如33333是5位,tn是前个位数a,sn是后个位数aa
int a,n,i=1,tn=0,sn=0,z=0; // i是用做循环的
printf("a = :"); // 输进位上的一个数
scanf("%d",&a);
z=a;
printf("n = :"); // 要有多少位
scanf("%d",&n);
do
{ tn = tn+a; //第一次tn是0,0+a是a ,之后a*10为a0,第2次tn是a ,a+a*10=aa ……
sn = sn+tn;
a = a*10;
++i;
}
while(i<=n);
printf("a+aa+aaa+...=%d\n",sn);
printf("%d+%d%d+%d%d%d+%d%d%d%d+.....的前 %d 项和是= %d\n",
z,z,z,z,z,z,z,z,z,z,n,sn);
return 0;
}
运行结果:a = : 3
n = : 5
a + aa + aaa + … = 37035
3+33+333+3333+……的前5项和是: 37035
注 意:
(1)循环体如果包含一个以上的语句,应该用大括弧括起来,以复合语句形式出现。如果不加大括弧,则while语句的范围只到while后面第一个分号处。例如,本例中while语句中若无大括弧,则while语句范围只到“ tn = tn+a; ” 。
(2)在循环体中应有使循环趋向于结束的语句。例如,本例中循环结束的条件是“ i<=n ”,因此在循环体中应该有使i增值以最终导致i>n的语句,这里用“ ++ i ; ”语句来达到此目的。如果无此语句,则i的值始终不改变,循环永不结束。
其实该例题用while语句也是一样的,大家可试试看。一般情况下,用while语句和用do-while语句处理同一问题时,若二者的循环体部分是一样的,它们的结果也一样。但在while后面的表达式一开始就为假(0值)时,两种循环的结果是不同的。例如:
例3、4 求1-10的累加和,用while和do - while循环比较
#include <stdio.h> #include<stdio.h>
main() main()
{ int sum=0,i; { int sum=0,i;
scanf(“%d”,&i); scanf(“%d”,&i);
while(i<=10) do
{ sum=sum+i; { sum=sum+i;
i++; i++;
} }
printf(“%d”,sum); while(i<=10);
printf(“%d”,sum);
} }
运行结果:输入 1 运行结果:输入 1
55 55
若输入大于10的数,结果就不一样了:
运行结果:输入 11 运行结果:输入 11
0 11
这是因为此时对while语句循环来说,一次也不执行循环体(循环体”while(i<=10)”为假,所以结果为0,而对do-while循环来说则要执行一次循环体,因此结果得11。
可以得到结论:当while后面的表达式的第一次的值为”真”时,两种循环得到的结果相同。否则,二者结果不相同(指二者具有相同的循环体的情况)。
3.3 for 语句
第二章例2、2是把华氏温度100°F转换成相应的摄氏温度的程序;如果要求输出一张华氏-摄氏温度转换表,例如:将华氏温度30-35°F之间的每一度都转换成相应的摄氏温度后输出,就要反复做多次温度转换计算和输出。在重复操作的过程中,温度每增加1°F,都会使用同一个计算公式和同一个程序过程。对于这类重复执行的问题,采用C语言的循环结构for语句,可以轻而易举地解决。
例3.5 输出一张华氏-摄氏温度转换表,华氏温度取值范围是[lower,upper],每次增加1°F。计算公式如第二章例2、2中所示。式中,c表示摄氏温度,f表示华氏温度。
#include < stdio.h >
int main()
{ // fahr表示华氏温度,celsius为摄氏温度,lower为华氏温度下限,upper为上限
int fahr, lower, upper;
double celsius;
printf("Enter lower输入华氏温度下限:");
scanf("%d",&lower);
printf("Enter upper输入华氏温度上限:");
scanf("%d",&upper);
printf("(fahr)华氏温度转换成 (celsius)摄氏温度是:\n");
for(fahr=lower;fahr<=upper;fahr++)
{ celsius=(5.0/9.0*(fahr-32));
printf("华氏温度%d = 摄氏温度%6.1lf\n",fahr,celsius);
}
}
输出结果:
Enter lower:30
Enwer lower:35
(fahr)华氏温度转换成 (celsius)摄氏温度是:
华氏温度30 = 摄氏温度 -1.1
华氏温度31 = 摄氏温度 -0.6
华氏温度32 = 摄氏温度 0.0
华氏温度33 = 摄氏温度 0.6
华氏温度34 = 摄氏温度 1.1
华氏温度35 = 摄氏温度 1.7
将程序的2次输入,改成一次输入,程序如下:
#include <stdio.h>
int main()
{ int fahr,lower,upper;
double celsius;
printf("输入华氏温度的下限(lower)和上限(upper):");
scanf("%d,%d",&lower,&upper);//下限值(30)和上限(35)一起输入,中间用”,”分开。
printf("fahr celsius:\n");
for(fahr=lower;fahr<=upper;fahr++)
{ celsius=(5.0/9.0*(fahr-32));
printf("%d %6.1lf\n",fahr,celsius);
}
}
程序设计流程如图3.3所示。
程序中用for语句实现循环,针对华氏温度在[lower,upper]内的每一个值,使用温度转换公式算出摄氏温度,并输出华氏温度和摄氏温度。温度的转换和输出是一个重复的操作,华氏温度每次增加1°F,直到超出给定的上限upper循环结束。
for语句中的fahr++ 相当于fahr = fahr + 1,即fahr的值增加1 。
在C语言中,for语句被称为循环语句,它可以实现C语言的重复执行。
for语句的一般形式为:for ( 表达式1; 表达式2; 表达式3 )
for语句中,用两个分号分隔3个表达式,但for的后面没有分号,因为for与其后的循环语句合起来作为一条完整的语句。
for语句的执行流程如图2、3所示,先计算表达式1;再判断表达式2;若值为“真”,则执行循环体语句,并接着计算表达式3,然后继续循环;若为“假”,则结束循环,继续执行for的下一条语句。
↓ ↓
fahr=lower 表达式1
↓ ↓
假
fahr<=upper? 表达式2
↓ 真 ↓
转换计算和输出 循环体语句
↓ ↓
fahr ++ 表达式3
for的下一条语句
图3.3 例2.7程序中for语句的执行流程图 图3.4 for语句执行流程图
for语句中的3个表达式以及循环体语句的执行顺序和书写顺序有所不同,计算表达式3在执行循环体语句之后。
图3.4清楚地表明:在fir语句的执行过程中。表达式2、循环体语句和表达式3将重复执行,而表达式1只在进入循环前执行一次。也就是说,for语句中的表达式1只执行一次。
在for语句中,常常通过改变和判断某个变量的值来控制循环的执行,这样的变量称为循环控制变量,简称循环变量。例如:华氏温度fahr就是循环变量,for语句的3个表达式分别对它赋初值、判断其值和改变其值。
for语句中3个表达式可以是任意合法的表达式,循环体语句只能是一条语句。
如果循环体语句由多条语句组成,必须用大括号把它们括起来,组成一条复合语句。
在C语言中,仅由一个分号(;)构成的语句称为空语句,它什么也不做。
如果将上述for语句改为:
for(fahr=lower;fahr<=upper;fahr++); //分号代表空语句
{ celsius=(5.0/9.0*(fahr-32));
printf("%d %6.1lf\n",fahr,celsius);
}
则循环体语句就是空语句,真正要反复执行的温度的转换和输出都被当做for的下一条语句。这也是初学者常犯的错误,程序运行时系统同样不会有任何出错提示。
不要在for语句中随意加分号(;)。
n
例3、6 输入一个正整数n,求 ∑i
i=1
这是一个反复求和的过程,在数学上可以表示为:sum=1+2+3 … +n,但无法直接表示成C语言的表达式。为了解决这个问题,首先抽取出具有共性的算式(称为循环不变式):
sum = sum + i ; sum是累加和,其初值为0。该算式重复n次,同时i从1变到n,就实现了从1加到n。设i为循环变量,确定for语句中的3个表达式和循环体语句:
(1)指定循环起点的表达式1: i = 1
(2)给出循环条件的表达式2: i <= n ( n是循环终点)
(3)设置循环步长的表达式3: i ++
(4)循环体语句: sum = sum + i
// 计算 1 + 2 + 3 + … + n
#include <stdio.h>
int main()
{ int i, n, sum;
printf("Enter n :");
scanf("%d",&n);
sum=0;
for(i=1;i<=n;i++)
{
sum=sum+i; //反复累加i的值
}
printf("Sum of numbers from 1 to %d is %d\n",n,sum);
return 0;
}
运行结果: Enter n: 100
Sum of numbers from 1 to 100 is 5050
虽然循环次数由输入的n决定,但就for语句而言,n的值在循环前已经决定。
由于sum = sum + i是在原累加和sum的基础上一步一步地累加i的值,所以在循环开始前,必须置为0,以保证sum在0的基础上累加,这个步骤千万不能遗漏。
循环体语句向右缩进对齐,可以明确标识循环体的范围,这和if语句的风格一致。
1 1 1
例3、7 输入一个正整数n,计算 1 - + - +…的前n项之和
3 5 7
//计算1-1/3+1/5-1/7+…共n项之和
#include <stdio.h>
int main()
{ int denominator,flag,i,n; // denominator表示分母,falg是一个改变符号的标志,i,n循环次数
double item,sum; // item是第i项的值,sum是累加和
printf("Enter n:"); // 提示
scanf("%d",&n); //输入一个整数,要计算多少项
flag=1; // 表示第一项的符号
denominator=1; // 表示第i项的分母,初始为1,视为 1/1
sum=0; // 置累加和sum的初值为0
for(i=1;i<=n;i++)
{ item = flag*1.0/denominator; // 计算第i项的值
sum = sum+item; // 累加第i项的值
flag = -flag; // 改变符号,为下一次循环做准备
denominator = denominator + 2; // 分母逐项加2
}
printf("sum = %f\n",sum); // 输出各项的累加和
return 0;
}
运行结果:Enter n: 2
sum = 0.666667
3.4 循环嵌套
一个循环体内又包含另一个完整的循环结构,称为循环嵌套。内嵌的循环中还可以嵌套循环,这就是多层循环。while语句、do-while语句、for语句都可实现循环嵌套,如:
while () for (; ;)
{ {
. .
. .
. .
while () while () *
{ . . . } { . . . } * * *
} } * * * * *
等等。 * * * * * * *
* * * * *
例3.8 打印如图3.5的图形 * * *
#include <stdio.h> *
void main() 图3.5 星号图形
{ int i, j, k; // 定义变量,用来控制循环
for(i=0;i<=3;i++) // 该循环用来输出上面4行 * 号
{ for(j=0;j<=2-i;j++) // 第一次输出3个空格,第二次i加了1,输出2个空格
printf(" "); // 显然第三次输出一个空格,第四次不输出空格
for(k=0;k<=2*i;k++) // 第一次输出一个*号,第二次k加了1,输出3个*号
printf("*"); // 显然第三次输出5个*号,第四次输出7个*号
printf("\n");
}
for(i=0;i<=2;i++) // 该循环输出下面3行 * 号
{ for(j=0;j<=i;j++) // 第一次输出1个空格,第二次输出2个空格,第三次输出3个空格
printf(" ");
for(k=0;k<=4-2*i;k++)//第一次i是0,输出5个 * 号,第二次输出3个*号,三次1个*
printf("*");
printf("\n");
}
return ;
}
例3.9 输出小九九
#include <stdio.h>
main()
{ int i,j;
for(i=1;i<=9;i++)
{
for(j=1;j<=i;j++)
{
printf("%d*%d=%d ",j,i,j*i);
if(i==j)
printf("\n");
}
}
return 0;
}
运行结果:
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
例3.9 五世纪末,我国古代数学家张丘建在他编写的《算经》一书里提出一个不定方程问题,即有名的“百钱买百鸡”问题。说:一只公鸡值5钱,一只母鸡值3钱,三只小鸡值1钱。用百钱买百只鸡,问公、母、小鸡各能买多少只?
我们在这里设:公鸡、母鸡、小鸡各为x、y、z个,显然x的变化范围在0和20之间,因为百钱全买公鸡也只不过买20只,同理y的变化范围在0~34之间,所得的不定方程为:
5x + 3y + z/3 = 100 既然是不定方程,所以其解也不只是一组,程序如下:
#include <stdio.h>
int main()
{
int x,y,z;
for(x=0;x<=20;++x)
{
for(y=0;y<=33;++y)
{//当x=0,y=25时,z自然=75,条件if((z%3==0)&&((5*x+3*y+z/3)==100)) 就成立,就打印公、母、小鸡数
//再循环x=4,y=18,z自然=78,… … … … 就成立, … … …
z=100-x-y;
if((z%3==0)&&((5*x+3*y+z/3)==100))
printf("cock(公鸡)=%d\t hen(母鸡)=%d\t chicken(小鸡)=%d\n",x,y,z);
}
}
return 0;
}
运行结果: cock(公鸡) = 0 hen(母鸡) = 25 chicken(小鸡) = 75
cock(公鸡) = 4 hen(母鸡) = 18 chicken(小鸡) = 78
cock(公鸡) = 8 hen(母鸡) = 11 chicken(小鸡) = 81
cock (公鸡)= 12 hen(母鸡) = 4 chicken(小鸡) = 84
趣 味 游 戏 题:
和计算机比赛,设有n坐山,你和计算机各为一方,双方轮流搬山,规定每次搬的山数不超过k坐,谁搬最后一坐山,就算谁输。
游戏开始时,计算机首先打印出:“你选择要搬的山的总数”,以及“每//次允许搬山的最大数(k)",然后请你先搬,等你从键盘上输入你要搬的总数后,计算机马上又打印出它搬的山数,并告诉你还余下多少坐山,并请你再搬,这样轮流搬山,直到最后一坐山搬完为止。计算机会告诉你这局谁赢了。接着它便准备同你玩下一局。你若不想继续玩了,可以在打入山的总数时输入"0",这时计算机便告诉你共玩了几局,双方胜负如何,并结束游戏。
计算机每次搬山遵循的原则是当:
剩余山数 -1<=可移动的最大数
时就移动(剩余山数-1)坐,以便把最后一坐山留给你。
由于对任意正整数 x , y一定有:
x%(y+1)<=y 因此对于我们的问题,计算机为了把最后一坐山留给你,而又要控制每次搬山数不超过最大数k,它取的数应该满足下列关系:
(n-1)%(k+1)
如果这样算出的数是0,即整除无余数,就规定搬1坐,以防出问题。如果摸清计算机的这种规律,你就可以采取相应措施,争取赢它。
#include <stdio.h>
#include <math.h>
void main() //n是搬山总数,k是搬山最大数,x你搬多少,y表示y= (n-1)%(k+1)
{ int n,k,x,y,cc,pc,g;// cc记载了计算机赢的局数,pc记载了人赢的局数,g记载了第几局游戏
printf("\t 搬 山 游 戏 程 序\n");
pc=cc=0;
g=1;
for(;;)
{ printf("请你选择要搬的山的总数:\n");
scanf("%d",&n);
if(n==0) break;
printf("请你选择每次允许搬山的最大数:\n");
rept: scanf("%d",&k);
if(k>n)
{ printf("超过了总数,错了!,重新输入:\n");
goto rept;
}
printf("\n 第 %4d 局 \n",g++);
printf("============================\n\n");
do
{ printf("你搬多少? ");
scanf("%d",&x);
printf("\n");
if(x<1||x>k||x>n)
{ printf("你搬的数非法,重新搬!\n");
continue;
}
n-=x;
if(n==0)
{ printf("***********计算机赢了!**************\n");
cc++;
}
else
{ y=(n-1)%(k+1);
if(y==0) y=1;
n-=y;
printf("计算机搬山数是:%2d\n",y);
if(n!=0)
printf("还剩的山数是: %2d\n",n);
else
{ printf("=======计算机失败了!=============\n");
pc++;
}
}
}
while(n!=0);
}
printf("共 玩 了 %5d局\n",cc+pc);
printf("计算机赢了%4d局\n",cc);
printf("你 赢 了 %5d局\n",pc);
}
练 习:
1、求sum = 1 + 3 + 5 + … + 9 的和。
1 1 1 1
2、求sum = 1 + - + - + - … - 的和。
2 3 4 n
2、求半径分别为:1、3、5、7、9的各个圆的面积。
3、从键盘上输入一些数,把这些数累加起来,输出累加和。以输入-1为结束符,可用
goto语句实现。
4、计算s = 1+4+7+10+…31的累加和(习题教材29页)。 求最大公约数和最小公倍数
(1)要求用while语句编出程序 2 18 24
(2)要求用do-while编出
展开阅读全文