C语言程序设计第六次作业
C语言程序设计第六次作业
标签(空格分隔): C语言
转眼间,我们又进入了新的一周。上一周我们学的主要是循环结构的while和do-while语句。这样,我们就将C语言中循环的主题语言掌握了。当然,我们在课堂上还学习了break和contini在循环语句中的具体使用方法。那么就让我们开始进行上一周C语言所学的总结吧。
(一)改错题
有错误的源程序如下:
#include<stdio.h>
int main(void)
{
int flag,n;
double eps,item,s;
printf("Input eps: ");
scanf("%f",&eps);
flag = 1;
s = 0;
n = 1;
do{
item = 1/ n;
s = s + flag * item;
flag = -flag;
n = n + 3;
}while( item < eps)
printf( "s = %f\n",s);
return 0;
}
题目要求为:
序列求和:输入一个正实数eps,计算序列部分和 1 - 1/4 + 1/7 - 1/10 + ... ,精确到最后一项的绝对值小于eps(保留6位小数)。
输入输出样例:
Input eps:1E-4
s = 0.835699
首先,我们将此源程序尝试编译一次,编译出错,而错误提示如下:
警告 C4477 “scanf”: 格式字符串“%f”需要类型“float *”的参数,但可变参数 1 拥有了类型“double *”(定位于第9行)
错误(活动) E0065 应输入“;”(定位于第18行)
错误 C2146 语法错误: 缺少“;”(在标识符“printf”的前面)(定位于第18行)
我们根据错误提示逐步修改:
-
do-while循环语句后需要分号结束。
-
在printf语句前没有分号造成语法错误。
造成这两个错误的原因相同,都是不熟悉do-while语句造成的。do-while语句与for语句和while语句的一个不同点就是for和while语句都类似于if语句,if(for,while)关键字所在的一行代码和其后的一行代码组合在一起,构成一个语句。而do-while语句不一样。
;是c语句的结束标志,while也有这个
while(循环条件)
{循环体};
do{循环体}while(循环条件);
总结错误原因为:不熟悉do-while语句,未在while后加上分号。
据此,修改意见为:在第19行while语句后加上分号";"。
- 当变量声明为double类型时,scanf语句中应该用%lf声明变原。%lf的变原类型为long*型。
总结错误原因为:声明为double类型却未将输入语句的格式说明符改为双精度所需要的格式说明符。
据此,修改意见为:第9行的%f
改为%lf
。
于是,此部修改后的源代码如下:
#include<stdio.h>
int main(void)
{
int flag,n;
double eps,item,s;
printf("Input eps: ");
scanf("%lf",&eps);
flag = 1;
s = 0;
n = 1;
do{
item = 1/ n;
s = s + flag * item;
flag = -flag;
n = n + 3;
}while( item < eps);
printf( "s = %f\n",s);
return 0;
}
此源代码运行结果如下:
我们看到结果为1.000000,这个结果为循环体运行一次的结果,我们推断是循环体的条件语句出现了问题。我们检查while括号中的判断语句发现错误,第一次的item值几乎一定大于我们所输入的那个比较小的精度,于是退出循环,while括号中是循环继续的条件而不是结束的条件。
最后总结得出错误原因:混淆了do-while语句后括号中的判断语句为真继续循环。
据此修改意见为:将第18行}while(item < eps)
修改为}while(item > eps)
。
运行结果为:
我们发现结果与之前相同,我推断是运算过程中出现了问题,据此,我们检查运算语句发现第14行的赋值语句后为两个int类型的变量在相除,而我们想要得到的却是浮点数类型。
据此,修改意见为:将item = 1 / n
改为item = 1.0 / n
。
运行结果为:
我发现程序运行结果已经很接近正确结果了,说明循环体的运行次数少了或多了一次,检查循环条件并根据题意:精确到最后一项的绝对值小于eps。即最后在一个数小于精确度时还需要将他加到结果中。
错误原因:未明白循环继续与结束的条件。(我的错误)
修改方法:第18行再次修改为}while( item >= eps )
。
最后正确源代码如下:
#include<stdio.h>
int main(void)
{
int flag,n;
double eps,item,s;
printf("Input eps: ");
scanf("%lf",&eps);
flag = 1;
s = 0;
n = 1;
do{
item = 1/ n;
s = s + flag * item;
flag = -flag;
n = n + 3;
}while( item >= eps);
printf( "s = %f\n",s);
return 0;
}
(二)学习总结
1.while(1)
和for(;;)
- while(1)的循环判断语句一直为真所以循环语句在没有break语句结束循环时将会一直循环下去。for(;;)语句也类似于while(1)都是无限循环。
for(;;)
和for(;1;)
相同。无限循环适用于不清楚循环应该运行的次数的循环,如寻找一个数,这种程序需要从1开始一直判断之后的数字是否符合条件。就像我们这周的PTA的第4题韩信点兵一样,无限循环是最简便的。 - 要想保证无限循环能够符合自己所期望的方式运行和得出结果,需要在合适的时机退出循环,要想实现这个目的我们可以使用break语句来退出循环,甚至有时可以使用goto语句。只要能保证逻辑性即可。
2.循环语句的选择
- (一)循环次数已知:for
使用for循环一般使语句块重复执行指定的次数。我们使用for循环的计数器来指定循环的次数。例如循环结构(二)的第2题直接指定将会输入的数据的数量据此,我们使用for语句循环。
第2题的源代码如下:
//2 猜数字游戏
#include <stdio.h>
int main(void)
{
int true_x = 0,try_max = 0,x = 0,try_num = 0;
scanf("%d%d",&true_x,&try_max);
//准备进入循环。
int flag = 0;
unsigned int i = 1 ;
for(; i <= try_max ; i++)
{
scanf("%d",&x);
if(x < 0)
{
printf("Game Over");
flag = 1;
break;
}
else if(x == true_x)
{break;}
else if(x > true_x)
{
try_num++;
if(i < try_max)
{printf("Too big\n");}
}
else
{
try_num++;
if("i < try_max")
{printf("Too small\n");}
}
}
//判断情况。
if(flag == 0)
{
if(try_num == 0)
{printf("Bingo!");}
else if(try_num < 3)
{printf("Lucky You!");}
else if(try_num < try_max)
{printf("Good Guess!");}
else
{printf("Game Over");}
}
return 0;
}
以及循环结构一中几乎所有指定了输入数字个数的题最适用的循环类型都是for循环。
- (二)循环次数未知,但循环条件在进入循环时明确:while语句
- (三)循环次数未知,且循环条件在进入循环时未知,需要在循环体中明确:do-while语句。
我认为,while语句和do-while语句的区分度很小,他们之间最主要的区别是do-while语句一定会运行一次,但大部分的题目或实际情况循环都会执行一次以上,而循环条件的判断时间也可以修改循环块内语句的顺序来达到同样的效果。例如我PTA的循环结构(二)中的4、5、6、7题都是用的do-while语句。而正常情况下却是do-while语句相较于其他两个语句并不是那么常用。
例如循环结构(二)的第七题需要使用无限循环,有肯定会进行至少一次循环,所以我们可以使用for、while、do-while语句中的任意一个,就如我们学习总结第一题所讨论的一样。
第7题源代码:
//250
#include <stdio.h>
int main(void)
{
int x = 0,i = 0;
//开始循环。
i = 1; //初始化i的值。
do{
scanf("%d",&x);
if(x == 250)
{
printf("%d\n",i);
break;
}
else
{
i++;
}
} while(1);
return 0;
}
而一般都用的while语句的第六题我却用的是do-while语句。
源代码如下:
//6.掉入陷阱的数字
#include <stdio.h>
int main(void)
{
int N = 0,i = 0,sum = 0,N1 = 0;
int digit1 = 0,digit2 = 0,digit3 = 0,digit4 = 0,digit5 = 0;
scanf("%d",&N);
i = 1; //初始化i的值,i为计数器。
int flag = 0;
do{
if(flag == 1)
{
N = N1;
goto Calculate;
}
else
{
Calculate: //接上述循环。
if(N <= 9)
{
sum = N;
}
else if(N <= 99)
{
digit1 = N % 10;
digit2 = N / 10;
sum = digit1 + digit2;
}
else if(N <= 999)
{
digit1 = N % 10;
digit2 = (N%100 - digit1) / 10;
digit3 = (N%1000 - digit2*10 - digit1) / 100;
sum = digit1 + digit2 + digit3;
}
else if(N <= 9999)
{
digit1 = N % 10;
digit2 = (N%100 - digit1) / 10;
digit3 = (N%1000 - digit2*10 - digit1) / 100;
digit4 = (N%10000 - digit3*100 - digit2*10 - digit1) / 1000;
sum = digit1 + digit2 + digit3 + digit4;
}
else
{
digit1 = N % 10;
digit2 = (N%100 - digit1) / 10;
digit3 = (N%1000 - digit2*10 - digit1) / 100;
digit4 = (N%10000 - digit3*100 - digit2*10 - digit1) / 1000;
digit5 = (N%100000 - digit4*1000 - digit3*100 - digit2*10 - digit1) / 10000;
sum = digit1 + digit2 + digit3 + digit4 + digit5;
}
N1 = sum*3 + 1;
printf("%d:%d\n",i,N1);
i++;
flag = 1;
}
} while(N1 != N);
return 0;
}
我这道题算法非常原始,更简便的算法是用一个循环来处理他。
while(N != 0)
{
digit = N % 10;
N = N / 10;
sum += digit;
}
一个循环就可以处理完我用了非常多的判断处理完的问题。
3.for、while、do-while样例
题目要求:输入一批学生成绩,以-1作为结束,计算学生的平均成绩。
用while语句的源代码如下:
#include <stdio.h>
int main()
{
double sum = 0.0, x = 0.0, average = 0.0;
int num = 0;
sum = 0.0;
num = 0;
while (1) //无限循环
{
scanf("%lf", &x);
if (x == -1)
{
break;
}
sum += x;
num++;
}
average = sum / num;
printf("平均成绩为%f", average);
return 0;
}
用for语句的源代码如下:
#include <stdio.h>
int main()
{
double sum = 0.0, x = 0.0, average = 0.0;
int num = 0;
sum = 0.0;
num = 0;
for(;;) //无限循环
{
scanf("%lf", &x);
if (x == -1)
{
break;
}
sum += x;
num++;
}
average = sum / num;
printf("平均成绩为%f", average);
return 0;
}
可以看到在使用while语句和for语句时十分相似,因为要使用的都是无限循环。下面我们再来看一看用do-while语句的样例:
#include <stdio.h>
int main()
{
double sum = 0.0, x = 0.0, average = 0.0;
int num = 0;
sum = 0.0;
num = 0;
scanf("%lf", &x);
if (x == -1)
{
average = 0.0;
}
else
{
do {
scanf("%lf", &x);
if (x == -1)
{
break;
}
sum += x;
num++;
} while (1);
average = sum / num;
}
printf("平均成绩为%f", average);
return 0;
}
使用do-while语句与使用for和while语句时不一样,因为需要排除用户直接在第一个数时输入-1的情况,所以会更加的复杂。
综合比较下来,在这道题上使用while语句比较合适。原因如下:
- 与for语句相比,看起来更加的直观。
- 与do-while语句的差距非常的明显,do-while语句需要更多的思考量并且程序更加的复杂,而原因就出在了do-while语句一定会运行至少一次,但有一些特殊情况会使得循环体不运行。
4.程序比较
源代码一如下:
#include<stdio.h>
int main()
{
int n,s,i;
s = 0;
for(i = 1; i <= 10; i++)
{
scanf("%d",&n);
if(n % 2 == 0)
break;
s = s + n;
}
printf("s = %d\n",s);
return 0;
}
源代码二如下:
#include<stdio.h>
int main()
{
int n,s,i;
s = 0;
for(i = 1; i <= 10; i++)
{
scanf("%d",&n);
if(n % 2 == 0)
continue;
s = s + n;
}
printf("s = %d\n",s);
return 0;
}
代码一输入1到10的结果如下:
代码二输入1到10的结果如下:
代码分析:
代码二明显比代码一更符合程序设计的目标,求十个数中奇数的和,而代码一运行的结果没有任何意义。原因就在于使用的是continue继续语句还是使用的是break跳出语句。我们也可以修改源程序一来实现预期的目的,但都不如continue语句简洁。
continue语句和break语句的区别:
我们最早接触break语句是在学习switch开关语句的时候,那个时候我们用break语句来结束某一个开关,否则switch语句的穿透性很容易造成错误。而在for、while、do-while循环中,作用则是跳出break语句所在的循环继续执行循环后的第一条语句。但continue语句不同,它并不会使循环彻底的结束,而只是将此次迭代中continue语句后的语句跳过,强制指定下一个迭代。continue语句主要来加快循环的运行。
(三)实验总结
1.求给定精度的简单交错序列部分和
本题要求编写程序,计算序列部分和 1 - 1/4 + 1/7 - 1/10 + ... 直到最后一项的绝对值不大于给定精度eps。
本题流程图如下:
本题源代码如下:
#include <stdio.h>
#include <float.h>
int main(void)
{
int flag = 0,t = 0;
double eps = 0.0,sum = 0.0,pre = 0.0;
scanf("%lf",&eps); //要求用户输入精度。
if(eps == 0.0) //判断用户是否输入非法数据。
{
printf("ERROR:请输入正确的精度。");
}
else
{
t = 1;
sum = 0;
flag = 1;
pre = DBL_MAX;
//开始进行循环。
while(pre > eps)
{
pre = 1.0/t;
sum += flag * (1.0/t);
t += 3;
flag = -flag;
}
printf("sum = %f",sum);
}
return 0;
}
这道题可能在循环条件上出现问题,题目要求将最后一位小于精度的数字加上,但如果用错了条件会让数据少加一次。可以修改循环体中的代码顺序来选择是否加上最后一位小于精度的数据,如将pre = 1.0 / t;
这一语句放在第27行就可以使得程序不将最后一组小于精度的数据加在结果中。
本题PTA提交列表如下:
2.猜数字游戏
猜数字游戏是令游戏机随机产生一个100以内的正整数,用户输入一个数对其进行猜测,需要你编写程序自动对其与随机产生的被猜数进行比较,并提示大了(“Too big”),还是小了(“Toos mall”),相等表示猜到了。如果猜到,则结束程序。程序还要求统计猜的次数,如果1次猜出该数,提示“Bingo!”;如果3次以内猜到该数,则提示“Lucky You!”;如果超过3次但是在N(>3)次以内(包括第N次)猜到该数,则提示“Good Guess!”;如果超过N次都没有猜到,则提示“Game Over”,并结束程序。如果在到达N次之前,用户输入了一个负数,也输出“Game Over”,并结束程序。
本题流程图如下:
本题源代码如下:
//2 猜数字游戏
#include <stdio.h>
int main(void)
{
int true_x = 0,try_max = 0,x = 0,try_num = 0;
scanf("%d%d",&true_x,&try_max);
//准备进入循环。
int flag = 0;
unsigned int i = 1 ;
for(; i <= try_max ; i++)
{
scanf("%d",&x);
if(x < 0)
{
printf("Game Over");
flag = 1;
break;
}
else if(x == true_x)
{break;}
else if(x > true_x)
{
try_num++;
if(i < try_max)
{printf("Too big\n");}
}
else
{
try_num++;
if(i > try_max)
{printf("Too small\n");}
}
}
//判断情况。
if(flag == 0)
{
if(try_num == 0)
{printf("Bingo!");}
else if(try_num < 3)
{printf("Lucky You!");}
else if(try_num < try_max)
{printf("Good Guess!");}
else
{printf("Game Over");}
}
return 0;
}
本题我使用的是for循环,因为指定了循环次数,于是必须在循环内判断一些需要跳出循环的条件。然后再在循环结束后根据循环内指定的一些变量的值的改变来判断循环进行了几次并输出相应的语句。
本题PTA提交列表:
3.求奇数和
本题要求计算给定的一系列正整数中奇数的和。
本题流程图如下:
本题源代码如下:
//3 求奇数和
#include <stdio.h>
int main(void)
{
int x = 0,sum = 0;
//开始循环
sum = 0; //sum再次赋初值0,因声明时已经初始化,所以可以省略。
while(1) //无限循环,用break语句来结束循环,do-while语句肯定会运行一次循环体,若第一个值就输入非法,不符合题目要求。
{
scanf("%d",&x);
if(x > 0)
{
if(x%2 != 0)
{sum += x;}
}
else
{break;}
}
//循环结束,输出结果。
printf("%d",sum);
return 0;
}
这道题相较第二题思维简单了不少。在无限循环中判断数据并在某一条件后跳出循环并输出数据。结果的输出一定要在循环结束后再汇总数据,题目并没有要求输出每一步的中间数据。
本题PTA提交列表如下:
(四)博客互评
我在李晓晓同学博客下的评论:
大佬!请收下我的膝盖,rapoter流程图太复杂了,我是真做不出来。而且大佬在学习总结环节还把程序每次运行的变量值用一个输出语句输出出来来调试程序,太专业了。
我在许天笑同学博客下的评论:
同学你的博客格式很规范,但是我认为你的实验分析可以填充一些文字使得博客显得更加充实。
我在程晓磊同学博客下的评论:
你终于把图片处理好了,之前你的图片都挂了的时候效果真的很搞笑。你的源代码其实可以用markdown带的格式来写而不是截图,或者是两者相结合起来,这样会更美观。
我在姚纪远同学博客下的评论:
老铁你的白色图片配上黑色背景显得图片和文字显得非常清楚,你的改错题每一个步骤非常清楚,并且步骤很足够。老铁你的id太牛了!不得不服啊!
我在赵晓辉同学博客下的评论:
同学你将源代码和运行结果都截图了下来,值得学习,但我认为一些单一的源代码用markdown的格式会更加的清晰。