C语言程序设计第六次作业
一:改错题:
将所给代码输入编译器,执行编译命令,错误如下图:
(1)错误一:
错误指向第18行:
所给错误信息显示在printf后缺少‘;’(分号),但是经过检查,printf后并未缺少符号,推想是printf前的语句有遗漏。
错误原因:在第17行的while语句后缺少分号。
改正方法:在while语句后补全“:”(分号)。
(2)错误二:
继续编译,系统并未报错,开始进入测试阶段。输入样例数据“1E-4”,结果如图:
不符合预期,再次输入其它数据“0.02”,结果如图:
可以推想出现了输出为常值的典型错误,初步推断为数型错误,返回检查。
错误原因:发现题中n定义为int型,且n参与除法运算,所以导致数型输出会出现问题。
改正方法:将计算式“item = 1 / n”改为“item = 1.0 / n”.
(3)错误三:
继续运行程序,样例数据不变,输出如下:
发现结果一样,推想是循环条件有误。
错误原因:while的循环判断条件有误,循环的执行条件为最后一项(item)大于精度(eps),而非小于。
改正方法:将while中的“item < eps”改为“item >= eps”。
(4)错误四:
继续运行程序,样例不变,发现结果如图:
发现无输出也未结束,进入无限循环,推想在浮点数的比较部分可能有误,返回检查之前的语句。
错误原因:输入精度(eps)的scanf语句中,格式化输入符号为应为对应double型的“%lf”,而不是“%f”,若不改正,会导致精确度有误。
改正方法:将输入单精度浮点数对应的“%f”改为对应双精度浮点数的“%lf”,再将最后的printf语句中的‘%f’改为“%.6f”,保证输出的为六位小数。
结果如图:
符合实验预期。改错结束。
另外,在改错过程中笔者遇到了另外一个问题,第一次修改while中的条件时,笔者推想浮点数比较的话,取等可能会出现误差,因此并未去取等号,即‘item = eps’,导致结果如下:
可以发现,与样例所给的数据相差了0.0001,使笔者十分费解,后来通过与老师交流与反复检查,发现问题正是出在这个“=”等号上,推想所给样例‘1E-4’正是该等号所对应的边界值,所以出现了这一预想外的误差,所以在比较时,应视情况取等。
二:学习总结:
(1)关于while(1)以及for(;;):
首先关于这两种无限循环的判断方式。
while()的括号内的判断方式实际上是判断括号内的条件是否为真,只要括号内为任意非零数值,均判断为真,其中可以是任意合法表达式,因此括号内输入‘1’,即永远为真,循环无限执行。
而对于for循环,其判断依据是根据for(;;)的括号内的三个表达式来判断真假,其中第一个表达式只执行一次,即给用于判断的值赋初值,第二个表达式则是用来判断真假,第三个表达式可以是任意合法表达式,通常情况下是输入关于某变量的自增自减函数,来实现有限次循环,当括号内三个表达式为空时,即判断条件为空,即条件永远为真,因此循环无限次执行。
这两种无限循环的使用方法基本没有差别,因为判断条件均永远为真,所以可以实现一些特定功能,但若要保证其正常执行,最常用的方式是设置某个标记变量,同时配合选择结构一起使用,当选择结构中某条件为真时,可以通过break语句跳出循环,实现结束循环的目的。此时可以保证循环的正常执行。
但是应当注意break语句的用法,其本质功能是结束距离break最近的一层循环结构,所以当涉及到循环的嵌套时,break的不正确使用往往会造成一些意料之外的结果,下面给出一段代码:
#include<stdio.h>
int main(void)
{
int i, j;
for ( i = 0; i < 10; i++ )
{
printf( "外部循环变量: i = %d
", i );
for ( j = 0; j < 3; j++ )
{
printf( "内部循环变量: j = %d
", j );
if ( i == 5 )
break; //原意是想在循环执行5次时,主循环体结束
}
}
printf( "循环结束: i = %d
", i );
Theend: printf( "执行Theend: i = %d
", i );
}
注意笔者加注释的部分,使用了break语句,原意是只令主循环执行五次就结束,结果如图:
但是从图中i,j的变化过程来看,并未实现预期的效果,这就是刚刚提到的break的实质,因为该程序中存在循环的嵌套,即关于i的循环内还存在关于j的循环,所以当执行break语句时,仅仅结束了内层循环的该次循环。
于是我们修改程序,如下:
#include<stdio.h>
int main(void)
{
int i, j;
for ( i = 0; i < 10; i++ )
{
printf( "外部循环变量: i = %d
", i );
for ( j = 0; j < 3; j++ )
{
printf( "内部循环变量: j = %d
", j );
if ( i == 5 )
goto Theend; //此处改用goto
}
}
printf( "循环结束: i = %d
", i );
Theend: printf( "执行Theend: i = %d
", i );
}
结果如图:
观察最后一句并观察i的值不难发现,当i的值为5时,整个循环结束,并输出最后一跳printf语句,同时发现,本应输出循环结束时i的值的语句消失,实际上这里就涉及到了goto语句,其用途是在程序中,强行将程序转移到具有goto后相同标号的部分,,又称为无条件转移语句,因此观察代码,可以发现笔者的标号处于输出最后一次i的值,即“循环结束”那一语句的后面,因此当i=5时,goto语句执行,因此循环结束,并且执行标号后的语句,最后程序结束。goto语句虽然在多循环嵌套时具有很大作用,但是原则上往往并不推荐使用,因为当涉及大量代码的编制时,goto语句会使代码的可读性变差,且容易造成混乱,这一点需要尤为注意。
(2)关于三种循环:
①在循环次数已知时,应当优先考虑for循环,首先注意一类问题,即给定项数求某数列和或某项的值,这类问题往往会给定某一上限,即已知循环次数,例如循环结构1中的第1(求奇数分之一序列和),7(求交错序列和)小题,这类问题只要得到适用性较强的通项公式,剩余的结构部分就较好处理。
对于另一类问题,例如统计型,在已知总数的情况下,处理一批数据,这类问题也可以用for循环解决,例如循环结构1中的2(统计学生成绩均值以及及格人数),3(奇偶分家),4(到底是不是太胖了),6(统计学生成绩)题,均属于已知数目的问题,此时使用for循环较为简单稳妥。
②在循环次数未知,但循环条件在进入循环时明确的情况下,应采用while循环,由于循环次数未知,但是已知某些条件,例如所求结果与另一变量间存在某种关系,或是已知某范围的情况下,均可采用while循环,尤其是涉及到穷举问题,求极限问题等,都优先考虑该种方法。例如循环结构2(猜数字游戏)中的第一题,即为给定精度,求和的问题,该问题的循环次数实际上为多少我们在不经过计算的情况下并不知道,但是我们知道精度与某项值的关系,因此可以在while中添加此条件,因而取得最佳的效果。
③循环次数未知,且循环条件在进入循环时未知,但在循环体中明确。
针对这种情况,优先采取的结构为do while结构,因为do while与while结构上最大的不同点在于while循环是进入循环前先判断一次条件,因此循环可能一次都不执行,而对于一些循环条件在进入时就未知的情况,再使用此种方法显然不太严谨,这就需要用到do while循环,因为do while循环的执行方式为先进入循环体执行一次循环体,之后再判断循环条件是否成立,这也保证了能避免一些极端条件的边界值问题,例如循环结构2中的第5(蠕虫)问题,以及第三题(求奇数和),这类问题的条件需要再进入循环体后再执行判断,因而选用do while结构较为稳妥。
但是相应的也要注意到,有些问题可以相互转化,例如循环结构2中的第一题,首先给出do while形式的代码:
#include<stdio.h>
#include<math.h>
int main(void)
{
int i = 1;
double sum = 0.0,eps = 0.0,x = 0.0,t = 1.0;
scanf("%lf",&eps);
do
{
x = t / (3 * i - 2);
sum += x;
t *= -1;
i ++;
}
while(fabs(x) > eps);
printf("sum = %.6f",sum);
return 0;
}
接着给出while结构的代码:
#include<stdio.h>
#include<math.h>
int main(void)
{
int i = 1;
double sum = 0.0,eps = 0.0,x = 1.0,t = 1.0;
scanf("%lf",&eps);
while(fabs(x) > eps)
{
x = t / (3 * i - 2);
sum += x;
t *= -1;
i ++;
}
printf("sum = %.6f",sum);
return 0;
}
实际上就功能实现上来说两种结构取得的效果完全相同,但是对于一些极端边界条件,使用do while较为合理,就上面给出的while形式的代码,由于笔者给x赋的初始值为1,因此当笔者输入任意大于等于1的值,均会出现如下结果:
因此,若要使用此循环,应在循环前添加额外的判断条件,因此较为繁琐,故不采用。
(3)给定一批成绩以-1为结束标志,计算平均成绩。
①先给出while循环结构:
#include<stdio.h>
int main(void)
{
double sum = 0.0,average = 0.0,score = 0.0;
int i = 1;
scanf("%lf",&score);
while(score >= 0)
{
sum += score;
scanf("%lf",&score);
average = sum / i;
i++;
}
printf("average = %.2f",average);
}
②接着是do while结构:
#include<stdio.h>
int main(void)
{
double sum = 0.0,average = 0.0,score = 0.0;
int i = 1;
scanf("%lf",&score);
if(score < 0) //判断用户输入的第一个值是否小于0,防止除零
{
average = 0.0;
}
else
{
do
{
sum += score;
scanf("%lf",&score);
average = sum / i;
i++;
}
while(score >= 0);
}
printf("average = %.2f",average);
}
③for循环:
#include<stdio.h>
int main(void)
{
double sum = 0.0,average = 0.0,score = 0.0;
int i = 1;
scanf("%lf",&score);
if(score < 0)
{
average = 0.0;
}
else
{
for(i = 1;score >= 0;i++)
{
sum += score;
scanf("%lf",&score);
average = sum / i;
}
}
printf("average = %.2f",average);
}
④最后是无限循环:
关于无限循环,实际上写法有多种,在前文也提到了,主要运用的形式为for(;;)以及while(任意非零值),只要注意结束循环的条件,无论哪一种,均可以取得较好的效果。下面给出for(;;)的形式:
#include<stdio.h>
int main(void)
{
int score = 0,sum = 0,count = 0;
for(;;)
{
scanf("%d",&score);
if(score < 0)
{
break;
}
else
{
sum += score;
count++;
}
}
if(count == 0) //防止用户第一个数输入小于0的值,防止除0
{
count++;
}
printf("average = %.1f
",(double)sum / count);
return 0;
}
对于另一种形式,即while(任意非零值),只需将for(;;)改为while(任意非零值)即可,不多赘述。
2)关于本题,笔者认为使用无限循环形式较好,因为对于这类实际问题,学生的实际数量并不是某一定值,而是有一定的浮动范围,因此使用for或while循环实现虽然并非不可能,但是相对无限循环有些舍近求远,而对于无限循环,只要指定一个某一特值或标记变量,即可实现随时退出的目的,灵活性更强。
(4)运行给定代码并分析:
1)首先运行第一段代码:
结果如下:
输入到2时循环结束。
2)接着运行第二段代码:
程序循环10次后结束,输出25.
3)原因分析:
首先分析第一段代码:
①定义三个整型变量n,s,i;
②给s赋初值0;
③i = 1,i <= 10判断为真,进入循环。
④,输入n = 1,n % 2 == 0为假。
⑤将n的值加和给s,s = 1。
⑥i = 2,循环判断为真。
⑦输入n,此时输入n = 2,,判断n % 2 == 0为真,执行break语句,跳出循环。
⑧输出s的值,此时s = 1;程序结束。
接着分析第二段代码:
①定义三个整型变量n,s,i;
②给s赋初值0;
③i = 1,i <= 10判断为真,进入循环。
④,输入n = 1,n % 2 == 0为假。
⑤将n的值加和给s,s = 1。
⑥i = 2,循环判断为真。
⑦输入n,此时输入n = 2,,判断n % 2 == 0为真,执行continue语句,但不执行s的加和语句,进入下一次循环。
⑧此时i = 3,循环条件为真,进入循环。
⑨输入n,n % 2 == 0为假,将s加和。
...................................
⑩1 = 11,循环条件为假,循环结束,此时s = 25,程序结束。
对于这两种情况的不同,根本原因在break与continue语句的不同,break语句执行时,程序会强制结束距离break语句最近的循环,而continue语句则是结束本次循环,并进入下一次循环的判断。
结合程序分析,对于程序1,当用户输入偶数时,if语句判断为真,执行break语句,循环结束并输出s的值,而对于程序2,当用户输入奇数时,continue语句不执行,并将该奇数加和给s,当用户输入偶数时,if语句判断为真,执行continue语句,但不执行下面的s的加和语句,即只将奇数加和给s。
因此单论功能的实现而言,程序的本意应为用户输入十个数值,并计算其中奇数的总和。
三:实验总结:
1:求给定精度的交错数列和:
1)流程图:
2)源代码如下:
#include<stdio.h>
#include<math.h>
int main(void)
{
int i = 1;
double sum = 0.0,eps = 0.0,x = 0.0,t = 1.0; //定义变量
scanf("%lf",&eps); //输入精度
do
{
x = t / (3 * i - 2); //计算x的值
sum += x;
t *= -1; //循环改编t的符号
i ++;
}
while(fabs(x) > eps); //循环条件
printf("sum = %.6f",sum); //输出sun的值
return 0;
}
3)实验分析:
本题主要使用do while循环结构,通过计算一次最后一项值的数值进入循环判断,最终当循环条件为假时结束循环。
先给出出现错误的代码:
#include<stdio.h>
#include<math.h>
int main(void)
{
int i = 1;
double sum = 0.0,eps = 0.0,x = 1.0,t = 1.0;
scanf("%lf",&eps);
while(fabs(x) > eps)
{
x = t / (3 * i - 2);
sum += x;
t *= -1;
i ++;
}
printf("sum = %.6f",sum);
return 0;
}
错误1:当笔者第一次写完时,输入样例数据均未出现错误,但当笔者输入1时,却出现如下结果:
输出为0,不符合预期与实际,推想为循环判断部分的边界值有误。
错误原因:x的初始值有误,导致当输入大于等于x的初值的数值时,循环不执行,而实际上精度无论为多大,x的首项均为1,sum也为1,即循环至少执行一次。
改正方法:将while循环改为do while循环。
4)本题提交列表:由于部分测试在编译器中完成,因此结果可能不符合实际情况。
2:猜数游戏:
1)流程图:
2)源代码:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int normal = 0,answer = 0,i = 0,N = 0,count = 0;
scanf("%d%d",&normal,&N); //输入正确的答案以及次数
for(i = 1;i <= N;i++)
{
scanf("%d",&answer); //输出回答
if(answer < 0) //当输入小于0的值时,游戏结束
{
break;
}
if(answer == normal) //此if后均为答案正确的情况
{
if(i == 1)
{
printf("Bingo!");
}
else if(i <= 3)
{
printf("Lucky You!");
}
else if(i <= N)
{
printf("Good Guess!");
}
exit(0); //正确时输出对应语句后直接结束程序
}
else if(answer > normal) //当回答错误但处于规定次数内时对用户输入数值进行比较
{
printf("Too big
");
}
else
{
printf("Too small
");
}
}
printf("Game Over");
return 0;
}
3)实验分析:
本题主要使用for循环结构,实际上难度并不大,但是由于包含情况分支较多,因此情况的遗漏较为常见,应提起注意:
错误一:笔者编译完毕后,输入样例数据“58 4 70 50 56 58”发现虽然输出了“Good Guess”,但也输出了不应输出的结果,如图:
推想是循环结束后,执行了循环外的不希望执行的语句,返回检查。
错误原因:笔者在对应三种正确情形的条件后,未加上使程序结束的语句,即遗漏‘exit(0)’,如图:
改正方法:将“exit(0)”补全。
本题提交列表:
3:求奇数和:
1)流程图:
2)源代码如下:
#include<stdio.h>
int main(void)
{
int number = 0,sum = 0;
do
{
scanf("%d",&number);
if(number % 2 != 0 && number > 0)
{
sum += number;
}
}
while(number > 0);
printf("%d",sum);
return 0;
}
3)实验分析:
本题主要使用do while结构以及if条件判断,难度不高。
所以...真的没有错误...
4)提交列表:
四:博客互评:
1:侯冠达:
http://www.cnblogs.com/HGD980425/p/7837319.html
答案完善,步骤很明确,就是字体需要修改一下,就好看了,整体很清晰明了,没毛病!!
2:许天笑:
http://www.cnblogs.com/snxtx/p/7824427.html
你的作业做的我觉得很棒,而且大部分的程序你都做了注释,这一点值得我的学习,希望晓晓同学能在学习上多帮帮我
ps:(你就睡我旁边的床,倒是来问我...)
3:郭玉霖:
http://www.cnblogs.com/HBQ521/p/7826030.html
作业写得我感觉很棒,值得学习。
4:刘恺煊:
http://www.cnblogs.com/liukaixuan/p/7855946.html
apoter流程图太复杂了,我是真做不出来。而且大佬在学习总结环节还把程序每次运行的变量值用一个输出语句输出出来来调试程序,太专业了。
(也就那么回事吧...多做做就行)
5:刘畅:
http://www.cnblogs.com/LLIU/p/7846322.html
写的很快很好,多向你学习!
(来一起学习)
6:胡展业:
http://www.cnblogs.com/SYDneyHZY/p/7824429.html
内容很充实,各方面都很好,学习学习。
7:李志伟:
http://www.cnblogs.com/666888i/p/7858064.html
内容很具体,解释的很详细。
8:**(希望下次注明姓名)
http://www.cnblogs.com/myg123/p/7841714.html
每次的博客写的都很详尽,而且提交作业的速度难以想象,整体较好,多多学习!
9:2614(同样希望注明姓名)
http://www.cnblogs.com/Maria2614/p/7860328.html
作业每次都很认真很详细,值得我学习
10:沐栀*(姓名......)
http://www.cnblogs.com/hbnydx/p/7858643.html
不仅完成了作业,还这么详细,多补充了这么多知识,我要向你学习!
11:郭展旭:
http://www.cnblogs.com/1234569ss/
通过大量测试数据验证边界值减少错误,更好的保证了程序的正确。值得学习