目录
循环结构又称重复结构,是按照一定的条件重复执行某段语句的程序控制结构。
基本的循环结构:
1)goto语句和if语句构成的循环结构;
2)while语句构成的循环结构
3)do-while语句构成的循环结构
4)for语句构成的循环结构
5.1 while语句
while语句用来构成当型循环,多用于解决循环次数事先不确定的问题。形式:
while(表达式)
{
循环体
}
功能:先判断表达式值的真假,若为真(非零)时,就执行循环体;否则,退出循环结构。
例:编程实现1+2+3+4+……+100。
#include "stdio.h"
main()
{
int a=1,sum;
sum=0;
while(a<=100)
{
sum=sum+a;
a++;
}
printf("%d",sum);
}
① 从何处来
② 到何处去
③ 如何修改循环变量在
④ 条件满足做什么事情
循环的初始值+循环条件+循环体+循环变量的增量+具有唯一真值的表达式
注意:
- while语句中的表达式一般是关系表达式或逻辑表达,也可以是数值表达式或字符表达式,只要其值为真(非0)即可继续执行循环体
- 循环体语句可以为任意类型,循环体如果包含一个以上的语句,应该用花括号起来,以复合语句的形式出现。如果不用花括号,则while语句的范围只到while后面第一个分号处。
- 在循环体中应该有使循环趋向于结束的语句,以避免死循环
- 允许while语句的循环体中包含另一个while语句,从而形成循环的嵌套
例:印数程序
#include "stdio.h"
main()
{
int a=0;
while(a<=1)
{
a++;
printf("%d\n",a);
}
}
while(n++<=1);
n++为前缀式,先判断n<=1,是否为真后,再加1;
如果++n,则先再1,再判断n<=1。
例:输入一行字符,按字母、数字、和其他分成三类,分别统计各类字符的数目('\n’)不在统计范围内。
#include "stdio.h"
#define IN 1
#define OUT 0
main()
{
int c,nl,nw,nc,state; //nc:字符数 nl:行数 //nw:字数
state=OUT;
nl=nw=nc=0;
printf("请输入内容并以ctrl+z为结束:\n");
while((c=getchar())!=EOF) //不等于-1,即为真
{
++nc;
if(c=='\n')
{
++nl;
}
if(c=='\t'||c==' '||c=='\n')
{
state=OUT;
}
else
{
if(state==OUT)
{
state=IN;
++nw;
}
}
}
printf("字符数:%d\n",nc);
printf("行 数:%d\n",nl);
printf("字 数:%d\n",nw);
return 0;
}
解析:
正文一行以'\n'为结束标志;
一个字以''为结束标志的一串字符;
EOF是End Of File的意思,在C语言中定义的一个宏,用作文件结束标志。从数值角度看,就是-1。从一个终端的输入从来不会真的“结束”(除非设备被断开),但把从终端输入的数据分区成多个“文件”却很有用,因此一个关键的序列被保留下来来指明输入结束
行数统计:对输入中的'\n'字符进行统计;
字数统计:对空格符''、制表符'\t'、换行符'\n'
字符数统计:对每个输入的字符(不包含EOF)进行计数;
算法:
(1)将标志位state置为初值0,表示字符不在子字,各计数器变量均为0;
(2)读一个字符c,如果不是EOF,则进行:
① 字符数+1;
② 若c是行数则行数+1;
③ 若c是空格符、换行符、制表符,则将标志置为0,表示c字符不在字中;
若c为非空白符,如果标志位为0(表示c是字符的第一个字符)则将字数+1,并修改标志位为1(表示当前字符c是一个字中的字符)
④ 循环步骤2
⑤ 如果c是EOF,则循环结束,转步骤3
(3)输出结果
穷举法:穷举法的基本思想是根据题目的部分条件确定答案的大致范围,并在此范围内对所有可能的情况逐一验证,直到全部情况验证完毕。若某个情况验证符合题目的全部条件,则为本问题的一个解;若全部情况验证后都不符合题目的全部条件,则本题无解。穷举法也称为枚举法。
(1)顺序列举 是指答案范围内的各种情况很容易与自然数对应甚至就是自然数,可以按自然数的变化顺序去列举。
(2)排列列举 有时答案的数据形式是一组数的排列,列举出所有答案所在范围内的排列,为排列列举。
(3)组合列举 当答案的数据形式为一些元素的组合时,往往需要用组合列举。组合是无序的。
从值域中取一个值,然后求其他两个数,满足条件就是解答。
5.2 do-while语句
直到型循环,多用于循环次数事先不确定的问题。
形式:
do
{
循环体
}while(表达式);
先执行一次循环体,再判断表达式的真假。若表达式为真(非0)则继续执行循环体,一直到表达式为假(0)时退出循环。
注意:while后面的“;”号不能少。
while与do-while的比较
while:先判断后执行
do-while:先执行后判断,语句中的循环体至少要被执行一次
当while后面的表达式的第一次的值为真时,两种循环得到的结果相同;否则,二者结果不相同。
5.3 for语句
可以用于循环次数已经确定的情况,也可以用于循环次数不确定而给出了循环结束条件的情况。
形式:
for(表达式1;表达式2;表达式3)
循环体
执行过程:
先求解表达式1,再求解表达式2,若真(非0),则执行for语句中指定的内嵌语句,然后求解表达式3;若表达式2为假(0),则结束循环;循环结束,执行for语句下面的一条语句。
for(循环变量赋初值;循环条件;循环变量增值)
{
循环体;
}
一般情况,等价于:
表达式1;
while(表达式2)
{
语句
表达式3;
}
注意:
- 一般格式为(p1;p2;p3),p1可以省略,分号不能省略;如“for(;i<=100;i++)”,省略的p1必须在for前面给予确定,即省略前给初值。
- p2可以省略,但要保留分号;这时无结束循环的条件,即循环不停地执行下去,成为死循环。如“for(i=1,sum=0;;i++)sum=sum+i”,省略的部分必须在循环体中给出
- p3后面没有分号,也可以省略。省略时,应在循环体内设置能改变循环变量值的语句,避免死循环。如“for(i=1,sum=0;i<=100;){sum=sum+i;i++}”
- 循环体可以是空语句,产生延时效果。如“for(i=0;i<5000;i++);”
- 虽然p1,p2,p3均能省略,但初学者不要尝试
例:求自然数前n项和。
#include "stdio.h"
main()
{
int sum=0,i,n;
printf("请输入:");
scanf("%d",&n);
for(i=1;i<=n;i++)
{
sum=sum+i;
}
printf("sum=%d",sum);
}
例:从键盘输入10个整数,求平均值。
#include "stdio.h"
main()
{
int i;
float f,sum;
for(i=0,sum=0.0; ;i++)
{
scanf("%f",&f);
sum+=f;
if(f==0.0)
{
break;
}
}
if(i==0)
{
printf("没有数据!");
}
else
{
printf("%f",sum/i);
}
}
例:求2000至2050之间的闰年。
#include "stdio.h"
main()
{
int i=2000;
for(i;i<=2050;i++)
{
if(i%4==0&&i%100!=0||i%100==0||i%400==0)
{
printf("%d ",i);
}
}
}
例:兔子繁殖。斐波那锲数列。
算法分析:
① 输入繁殖的总时间
② 繁殖的起始时间是3;1、2月数量是1
③ 从3月直到n月结束,前两个月的总数等于第三个月
即f=f1+f2。输出第三个月的数量即可。
#include <stdio.h>
main()
{
int n; //月(总时间)
long int f,f1,f2; //f=f1+f2
int i=3; //3月开始繁殖 起始月
f1=1;
f2=1;
printf("请输入月:");
scanf("%d",&n);
for(i;i<=n;i++)
{
f=f1+f2; //第3项=前两项的和
f1=f2; //放第一个数字
f2=f; //放第二个数字
printf("%5ld",f);
}
}
例:放米粒。
算法分析:
① 循环变量初值n是0;sum作为存放器;t做为平方变量
② n如果小于64,则向sum中放入数,t再此基础乘2,作为平方变量。
注意:sum、t应用double类型,数据较大
#include "stdio.h"
main()
{
int n; //循环变量
double sum,t;
sum=0.0;
t=1.0;
for(n=0;n<64;n++)
{
sum=sum+t;
t=t*2;
}
printf("%e",sum);
}
5.4 三种循环的比较
- 一般情况下可相互代替
- while循环结构:
- 只设置了结束循环的条件,循环体内需要设置打破循环条件而使循环趋向结束的语句。
- do-while循环和while循环相似,但do-while循环运行循环体,然后再进行循环结束条件的测试,循环体至少要执行一次
- 对于已知重复次数的循环,使用for结构更加方便、清晰。
- 仅知道循环结束的条件,不知道循环次数的用while循环和do-while循环更简洁
5.5 循环的嵌套
算法分析:
- 输入n
- 采用二重循环,外循环控制项目,内循环控制连乘次数
- 外循环:如果i<n(即i小于输入的n),将连乘次数置1,控制内循环的j也置1
- 内循环:如果j<i(即j小于循环的次数),则连乘次数乘i次,用term存放结果;而i的变化取决于n的值
- 如果内循环j>i,则跳出内循环,执行sum
- 外循环控制内循环
#include "stdio.h"
main()
{
int i,j,n; //i是底;n是键入;j控制内循环
long sum,term; //各项之和sum;i的次方term
sum=0,i=1;
printf("请输入n:\n");
scanf("%d",&n);
for(i;i<=n;i++)
{
term=1;
j=1;
do
{
term=term*i;
}
while(++j<=i);
sum=sum+term;
}
printf("%ld",sum);
}
例:求整数3~100中的素数。
算法分析:
- n是整数的范围0至100;i是条件
- 外循环for,取值范围从3循环至100
- 内循环while,判断是否为素数;如果是则将其输出
#include "stdio.h"
main()
{
int n,i,a;
n=3;
for(n;n<=100;n++)
{
i=2; //i=2条件
while(n%i!=0)
{
i++;
if(i==n)
{
printf("%d ",n);
a++;
}
}
}
printf("\n个数为:%d",a);
}
例:输入一个字母,输出由这个字母决定其高度的字符“金字塔”
算法分析:
- 外循环变量c1;并列内循环变量c2
- 输入字符如果是小写,top置为a;大写top置为A;否则置为0
- top非0;打印图形
- ① 外循环c1控制行数;c1<=c,则输出一行
- ② 内循环输出4个内容:左边所有的空格、一行前半段、一行后半段、换行
- ③ c1=c1+1,继续输出下一行
- c1>c,则结束循环
注意:内循环中的每一个for循环,结束后都重新开始。
#include "stdio.h"
#include "ctype.h"
main()
{
printf("请输入字符:\n");
char c,top,c1,c2;
int i;
top=isupper(c=getchar())?'A':(islower(c)?'a':'\0');
if(top)
{
for(c1=top;c1<=c;++c1)
{
for(i=c;i>=c1+1;--i)
{
putchar('-');
}
for(c2=top;c2<=c1;++c2)
{
printf("%c",c2);
}
for(c2=c1-1;c2>=top;--c2)
{
printf("%c",c2);
}
printf("\n");
}
}
}
例:打印输出乘法表
算法分析:
- 输出表头部分:*、1-9、换行
- 输出表体
① 外循环i控制行号;初值为1,即外循环控制行数
② 如果i<=9,就输出一行:
输出行号(被乘数);
列号j(乘数)从1~9;如果j>=i则输出i*j,否则输出空格;
换行;
继续循环
- 如果i>9就结束
#include "stdio.h"
main()
{
int j,i; //i表示行号;j表示列
printf("%4c",'*');
for(i=1;i<10;++i)
{
printf("%4d",i);
}
printf("\n");
for(i=1;i<=9;i++)
{
printf("%4d",i);
for(j=1;j<10;++j)
{
if(j>=i)
{
printf("%4d",i*j);
}
else
{
printf("%4c",' ');
}
}
printf("\n");
}
}
多重循环的使用:
- 给与循环有关的变量赋初值:只需执行一次的赋初值操作,应放在最外层循环开始执行之前。
- 对内层循环的变量赋初值应该放在内层循环开始执行之前、外层循环的循环体内
- 内、外循环不能同名,否则将造成循环控制的混乱,导致死循环或计算结果错误
- 正确的书写:在内循环中执行的所有语句必须用{}括起来组成复合语句作为内循环体;外层循环的语句应放在内循环体之外、外循环体之中
- 不应在循环中执行的操作应放在进入最外层循环之前或最外层循环结束之后
5.6 break语句和continue语句
break和continue语句一般将其放在if语句中
break语句:
遇到break语句。循环将无条件终止,程序跳出循环结构。三种循环都可以使用break
break可跳出for{}循环
continue语句:
终止本次循环,continue语句后面的语句不执行而进入下一次循环
break语句只能跳出一重循环;
在while和do-while语句中,continue语句将使程序直接转向条件测试处,当为真是进入下一轮循环。
在for语句中,将首先执行循环控制变量的增值表达式,然后再判断条件表达式,当为真是进入下一轮循环。
例:从键盘输入任意个数,求其平均值,当输入值为0时,计算结束。
#include "stdio.h"
main()
{
int i;
float f,sum;
for(i=0,sum=0.0; ;i++)
{
scanf("%f",&f);
sum+=f;
if(f==0.0)
{
break;
}
}
if(i==0)
{
printf("没有数据!");
}
else
{
printf("%f",sum/i);
}
}
例:用break语句打印九九乘法表
#include "stdio.h"
main()
{
int i,j; //i控制行号;j控制列
for(i=1;i<=9;i++)
{
for(j=1;j<=9;j++)
{
if(i<j)
{
break;
}
printf("%d*%d=%-4d",i,j,i*j);
}
printf("\n");
}
}
例:输出100以内能被7整除的数。
#include "stdio.h"
main()
{
int i,j;
for(i=1;i<=100;i++)
{
if(i%7!=0)
{
continue;
}
printf("%4d",i);
}
}
5.7 综合案例
列举算法
根据提出的问题,列举所有可能的情况,并根据条件检验哪些是需要的,哪些是不需要的。关键是根据问题的性质确定判定的条件,从而对列举的所有条件进行判断。
试探算法
列举量事先不知,只能从初始情况开始,往后逐步进行试探,直到满足给定的条件为止。
#include "stdio.h"
main()
{
int n,flag,x,k,a,b,c,d,e; //k是孩子;abced是五个小孩分到的苹果
n=11; //n是苹果
flag=1;
while(flag)
{
x=n;
flag=0;
for(k=1;k<=4&&flag==0;k++)
{
if((n+1)%(k+1)==0)
{
n=n-(n+1)/(k+1);
}
else
{
flag=1;
}
}
if(flag==0&&n!=11)
{
flag=1;
}
n=x+1;
}
printf("总苹果:%d\n",x);
a=(x+1)/2;
b=(x-a+1)/3;
c=(x-a-b+1)/4;
d=(x-a-b-c+1)/5;
e=11;
printf("A=%d\n",a);
printf("B=%d\n",b);
printf("C=%d\n",c);
printf("D=%d\n",d);
printf("E=%d\n",e);
}
- 假设苹果的数量为x,设5个小孩分苹果的数为a,b,c,d,e a=(x+1)/2;b=(x-a+1)/3…
- 设当前试探点苹果数量为n:第k个小孩得到全部剩下苹果的:(k+1)/1+k(k+1)/1 ;即(n+1)/(k+1)个苹果。可被整除。
- 发完第k个小孩后,剩下的苹果为n-(n+1)/(k+1)=>n
- 进行4次分配,如果每次分配时均满足其中的条件,并且剩下11个苹果,则试探的n即为原来的苹果数x。
- while循环进行试探,for循环模拟4次的发放。
- 当4次模拟条件均满足,且最后剩下11个苹果,则当前试探的n即为原来的苹果数x5
密码问题
#include "stdio.h"
main()
{
char c;
int k;
printf("请输入k的值:");
scanf("%d",&k);
getchar();
c=getchar();
while(c!='\n')
{
if((c>='a'&&c<='z')||(c>='A'&&c<='Z'))
{
c=c+k;
if(c>'z'||(c>'z'&&c<='z'+k))
{
c=c-26;
}
}
printf("%c",c);
c=getchar();
}
}