C语言程序设计第四次作业
C语言程序设计第四次作业
标签(空格分隔): C语言
在新的一周的9:49,我们亲爱的老师又把新一周的作业发表了。 〒▽〒,知道这一刻总是要到来的,但还是让人伤心 ╥﹏╥...。
(一)改错题
这一题让我们判断输入的三个数能否构成一个三角形,可以的话就输出周长和面积,不可以的话就输出错误信息。
错误源代码如下:
#include <stdio.h>
#include <math.h>
int main(void)
{
double a,b,c;
double area,perimeter,s;
printf("Enter 3 sides of the triangle: ");
scanf( "%lf%lf%lf",&a,&b,&c );
if( a + b > c || b + c > a || a + c > b)
s = ( a + b + c ) / 2;
area = sqrt( s * ( s - a ) * ( s - b ) * ( s - c );
perimeter = a + b + c;
printf("area = %.2f; perimeter = %.2f\n",area,perimeter);
else
printf("These sides do not correspond to a valid triangle\n");
return 0;
}
(第一次修改)我们首先运行一下这个源程序,错误信息如下:
--------------------配置: mingw5 - CUI Debug, 编译器类型: MinGW--------------------
检查文件依赖性...
正在编译 E:\Begining C\测试\main.c...
[Error] E:\Begining C\测试\main.c:14: error: syntax error before ';' token(定位于第14行)
[Error] E:\Begining C\测试\main.c:18: error: syntax error before "else"(定位于第18行)
构建中止 main: 2 个错误, 0 个警告
我们根据编译器的提示信息检查第14行和第18行发现小括号的对应不正确,缺少一个“)”。再次联想if else语句的括号可能出现问题再次检查果然发现if( a + b > c || b + c > a || a + c > b)
后所跟语句并没有加{}
if语句后只能跟一段语句,而”{}“是为了让几个语句判断为一个语句。但不加”{}“后不仅if语句不能执行,连else也不能正确与if配对。
修改意见:将第13行至第16行包含至一个大括号中,并且在第13行分号之前加一个“)”
修改后代码:
#include <stdio.h>
#include <math.h>
int main(void)
{
double a,b,c;
double area,perimeter,s;
printf("Enter 3 sides of the triangle: ");
scanf( "%lf%lf%lf",&a,&b,&c );
if( a + b > c || b + c > a || a + c > b)
{
s = ( a + b + c ) / 2;
area = sqrt( s * ( s - a ) * ( s - b ) * ( s - c ));
perimeter = a + b + c;
printf("area = %.2f; perimeter = %.2f\n",area,perimeter);
}
else
printf("These sides do not correspond to a valid triangle\n");
return 0;
}
(第二次修改)再次编译发现已经编译成功,进行案例测试的结果如下:
输入5 5 3:
输入1 4 1:
可以看到在输入5 5 3时程序给出的答案正确,在输入1 4 1这组不能构成三角形的数据时我们我们看到area的结果错误推断是垃圾值,我们将定义的变量初始化检测一下:
结果证明这并不是垃圾值,我们根据这个可以去尝试检查输出语句和输入语句甚至需要检查我们的赋值语句。
经检查,并未检查出明显错误。推断是因为根号中的数不能为负数, 而要使sqrt语句中不会是负数只能完善
判断语句。将判断语句中的或都改为且。即将if( a + b > c || b + c > a || a + c > b)
改为 if( a + b > c && b + c > a && a + c > b)
尝试运行结果如下:
输入5 5 3:
输入1 4 1:
发现程序已经可以正确识别不能构成三角形的数据说明我们的本次修改正确。
(第三次修改)我们的程序已经满足题目所给的案例的条件。但很可能还有问题,我再次全篇检查发现几个小问题如下:
第14行的s = ( a + b + c ) / 2;
语句处以2不严谨,虽然/是二元运算符(就sizeof,+,-,!,++,--,~是一元运算符,说这有凑字嫌疑。)会自动转化为double型但把2改为2.0或者2.都更有益于自己或者其他人的理解。
修改意见:将 s = ( a + b + c ) / 2;
改为s = ( a + b + c ) / 2.0
修改完成,最终源程序如下:
#include <stdio.h>
#include <math.h>
int main(void)
{
double a = 0.0,b = 0.0,c = 0.0;
double area = 0.0,perimeter = 0.0,s = 0.0;
printf("Enter 3 sides of the triangle: ");
scanf( "%lf%lf%lf",&a,&b,&c );
if( a + b > c && b + c > a && a + c > b)
{
s = ( a + b + c ) / 2;
area = sqrt( s * ( s - a ) * ( s - b ) * ( s - c ));
perimeter = a + b + c;
printf("area = %.2f; perimeter = %.2f\n",area,perimeter);
}
else
printf("These sides do not correspond to a valid triangle\n");
return 0;
}
最终运行结果如下:
输入5 5 3:
输入1 4 1:
输入3 4 5:
到此,这个程序已经可以交货了。(o)/
(二)学习总结
这一周我们学习的主题就是条件判断,条件判断所用的语句主要分为基本的if语句,扩展if语句:if-else,嵌套的if语句,多项选择:else if语句,goto语句,按位运算符:op=。这6类,而其中4种都是需要用到if和扩展if的。而if又需要逻辑运算符和逻辑非运算符。
而关于我们对if、if-else、if连用、switch的选择我认为主要区分以下几点:
- 当只有一个简单的判断时只需要if甚至if后只有一条语句。
- 当几个条件有比较强的联系时一般是用if-else语句。
- 当多个条件需要判断,但这几个条件之间的关联并不多时,也就是这几个条件的判断互不影响时,if连用的情况会更多。
- 而switch的应用范围就比较小了,它只能判断整数是否符合条件。但switch语句的穿透性的用法非常有趣,如果应用得当可以极大的简化程序。同时,在对较多数据的判断时,switch语句真的会更适应。
关于条件判断语句:我们学习条件判断语句需要注意许多细节的点如else会和哪一个if语句配对?if()后不能加分号,if只能涵盖一条语句但{}可以将几条语句合为一个代码块来归到一个if后面。要养成在每一个if、else if、else后加代码块,无论有几条语句的习惯。
关于逻辑运算符和逻辑非运算符:最简单却又最无奈的一点是=
和==
的区别,别看这个点很简单,它却很容易造成很严重的后果。以至于《C缺陷与陷阱》的1.1就是=不同于==,足以窥见这个错误有多么常见。
第二点就是我们一定要搞清或且非这三种逻辑的异同点。什么时候用或什么使用用且,这些事我们高中数学非常熟练运用过的东西,在大学我们更不能犯这种错误了。
在这一周里我们还学习了switch语句(开关语句),虽然它能做到的if语句都能做到,但它却能让一些问题简化。
C语言虽然没有限制 if else 能够处理的分支数量,但当分支过多时,用 if else 处理会不太方便,而且容易出现 if else 配对出错的情况。-来自网络
在使用开关语句是我们需要注意的几个问题分别是:
- switch只能判断整数,不能判断小数。
- switch后的括号内是要与后面case语句的常量比较的数。
- 开关语句具有穿透性,注意break的合理使用。有些情况下也会利用开关语句的穿透性简单的解决问题。
- default后并不需要break。
- 开关语句中可以没有default。
我们甚至还在课堂上扩展了rand和srand语句的使用来让我们能够有随机数来参与到我们的程序中来。其中还告诉了我们用时间作为种子是最简单的方法,且需要调用系统时间就需要标准库头文件:stdlib.h
的支持。我们在课堂上说了一种随机数的实际应用:给熊孩子出简单的四则运算题( ̄へ ̄ )。可以说是课堂作业吧,我也把这个有趣的作业做完了,在这里提交:
//熊孩子,我送你一整套53好不好啊?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int a = 0,b = 0,t = 0,x = 0;
double y1 = 0.0,y2 = 0.0;
srand((unsigned)time(NULL));
x = rand()%4;
a = rand()%10;
b = rand()%10;
if(a < b)
{
t = a;
a = b;
b = t;
}
if(x = 0)
{
y1 = a + b;
printf("请计算%d+%d的结果为:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正确!");
}
else
{
printf("答案错误,正确答案为:%f",y1);
}
}
else if(x = 1)
{
y1 = a - b;
printf("请计算%d-%d的结果为:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正确!");
}
else
{
printf("答案错误,正确答案为:%f",y1);
}
}
else if(x = 2)
{
y1 = a * b;
printf("请计算%d*%d的结果为:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正确!");
}
else
{
printf("答案错误,正确答案为:%f",y1);
}
}
else
{
y1 = (double)a / (double)b;
printf("请计算%d/%d的结果为:",a,b);
scanf("%lf",&y2);
if(y1 == y2)
{
printf("答案正确!");
}
else
{
printf("答案错误,正确答案为:%f",y1);
}
}
return 0;
}
运行结果如下:
等到我们学习循环语句后我们就可以设定生成几次题了,或者在现在想要生成几道题就把部分语句复制粘贴几次。
(三)实验总结
我的学号是5,需要总结的题是1、5、7、9题,那么就让我们开始吧。
1.成绩转换
本题要求编写程序将一个百分制成绩转换为五分制成绩。
- 大于等于90分为A;
- 小于90且大于等于80为B;
- 小于80且大于等于70为C;
- 小于70且大于等于60为D;
- 小于60为E。
Raptor流程图如下:
源代码如下:
//1.成绩转换
#include <stdio.h>
int main(void)
{
long long x = 0ll;
//printf("请输入你的成绩。\n");
scanf("%lld",&x);
x = x / 10;
switch(x)
{
case 10:
printf("A\n");
break;
case 9:
printf("A\n");
break;
case 8:
printf("B\n");
break;
case 7:
printf("C\n");
break;
case 6:
printf("D\n");
break;
default:
printf("E\n");
}
return 0;
}
这道题既可以用if-else语句来写也可以用swithch语句(开关语句)来写,但是题目要求我们练习开关语句的使用,而这道题的难易度也是为了让我们来适应新语句而设置的。
而我们需要注意的一点就是switch语句只能用于整数型变量,也就是声明时需要交给switch判断的变量需要声明成(unsigned)char,(unsigned)short,(unsigned)int,(unsigned)long,(unsigned)long long中的一种,否则编译器会产生错误信息。
其次,我们一定要记得在每一行语句后加上break否则我们会得到所应得到的等级和它以下的每个等级。
还有一点,这道题我们可以不使用default。或者将E等级不用case而用default来实现表达。
我的PTA提交列表:
5.三角形判断
给定平面上任意三个点的坐标(x1,y1)、(x2,y2)、(x3,y3),检验它们能否构成三角形。
Raptor流程图如下:
这道题的源代码如下:
//5.三角形判断
#include <stdio.h>
#include <math.h>
int main(void)
{
double x_1 = 0.0,x_2 = 0.0,x_3 = 0.0;
double y_1 = 0.0,y_2 = 0.0,y_3 = 0.0;
double distance_ab = 0.0,distance_ac = 0.0,distance_bc = 0.0,A = 0.0,L = 0.0;
//printf("三个点的坐标x1、y1、x2、y2、x3、y3。中间用空格隔开。");
scanf("%lf%lf%lf%lf%lf%lf",&x_1,&y_1,&x_2,&y_2,&x_3,&y_3);
distance_ab = sqrt(pow(x_1-x_2,2)+pow(y_1-y_2,2));
distance_ac = sqrt(pow(x_1-x_3,2)+pow(y_1-y_3,2));
distance_bc = sqrt(pow(x_2-x_3,2)+pow(y_2-y_3,2));
if(distance_bc < distance_ac+distance_ab&&distance_ac < distance_bc+distance_ab&&distance_ab < distance_bc+distance_ac)
{
L = distance_ab + distance_ac + distance_bc;
A = sqrt(L/2.0*(L/2.0 - distance_ab)*(L/2.0 - distance_ac)*(L/2.0 - distance_bc));
printf("L = %.2f, A = %.2f\n",L,A);
}
else
{
printf("Impossible\n");
}
return 0;
}
实验总结:
- 第一点:这道题其实可以将x1,x2,x3,y1,y2,y3,都定义为int类型的,这样就可以一定程度的简化思维和程序优化,例如:%lf就可以用我们最常用又不容易出错的%d代替了。
- 第二点:这道题要记得在添加头文件的时候加上数学头文件(math.h),这道题是一定会使用一些数学函数的,例如求根号sqrt。不仅如此,使用数学增加代码的可读性,例如平方可以用pow代替连乘。
现在我们已经学会使用不少头文件了,现在让我们整理一下吧:
- stdio.h:我们最常用的,定义了用于输入输出的宏和函数,从键盘上读取数据,将输出写入命令行上时,需要包含这个头文件。
- float.h:它定义的宏设置了浮点数的限制和属性。
- limits.h:它定义的宏扩展了一些值,以定义标准整数类型的限值。
- math.h:定义了用于常见数学操作的函数。
- stdlib.h:定义了许多一般用途的函数和宏。它包含了将字符串转化为数值的函数,生成伪随机数的rand()函数,给数据动态分配和释放内存的函数,搜索和排序例程,整数算术函数,以及转换多字节和宽字符串的函数。(真是一个高深的函数)
- time.h:它定义的宏和函数支持日期和时间操作。包括在程序执行过程中确定过去了多长时间。
我应该多复习以前的知识,也许现在有些看着不是那么重要,但今后一定会在很重要的地方帮上我们的。
PTA此题提交列表:
因为是在实验课上时做的,所以一次性的就通过了。
7.两个数的简单计算器
本题要求编写一个简单计算器程序,可根据输入的运算符,对2个整数进行加、减、乘、除或求余运算。题目保证输入和输出均不超过整型范围。
Rapor流程图如下:
此题源代码如下:
//7. 两个数的简单计算器
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int a = 0,b = 0,result = 0;
char c = ' ';
//printf("输入在一行中依次输入操作数1、运算符、操作数2,其间以1个空格分隔。操作数的数据类型为整型,且保证除法和求余的分母非零。");
scanf("%d %c%d",&a,&c,&b);
switch(c)
{
case '+':result = a + b;break;
case '-':result = a - b;break;
case '*':result = a * b;break;
case '%':
if(b == 0)
{
printf("ERROR");
exit(0);
}
else
{
result = a % b;
}
break;
case '/':
if(b == 0)
{
printf("ERROR");
exit(0);
}
else
{
result = a / b;
}
break;
default:printf("ERROR\n");exit(0);
}
printf("%d\n",result);
return 0;
}
实验总结:
- 这道题相比之前的题新增了开关语句(switch语句)的知识点,这道题同时也可以用if语句来写,但老师为了让我们熟悉新知识而让我们用switch语句来写。
- 注意这道题的除法中被除数不能为零,虽然PTA中的题目没有做此要求,但在今后的实际应用时是必须考虑到各种各样的问题的。我这道题的Raptor没有将被除数不能为零判断出来,谢谢同学们的指出:现修改为:
- 其实这道题的resault声明为浮点数并修改之后的程序会更加适应实际情况,但题目已经说明会给出的测试数据会符合整数类型,所以我们用int来声明就已经足够。
再此,我再补上这道题用判断语句的源代码:
//7. 两个数的简单计算器
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int a = 0,b = 0,result = 0;
char c = ' ';
//printf("输入在一行中依次输入操作数1、运算符、操作数2,其间以1个空格分隔。操作数的数据类型为整型,且保证除法和求余的分母非零。");
scanf("%d %c%d",&a,&c,&b);
if(c == '+' || c == '-' || c == '*' || c == '/' || c == '%')
{
if(c == '+')
{result = a + b;}
if(c == '-')
{result = a - b;}
if(c == '*')
{result = a * b;}
if(c == '/')
{
if(b == 0)
{printf("ERROR");}
else
{result = a / b;}
}
if(c == '%')
{result = a % b;}
}
else
{printf("ERROR");}
printf("%d\n",result);
return 0;
}
PTA提交列表:
9.求一元二次方程的根
本题目要求一元二次方程的根,结果保留2位小数。
本题Raptor流程图如下:
本题源代码如下:
// 9.求一元二次方程的根
#include <stdio.h>
#include <math.h>
int main(void)
{
double a = 0.0, b = 0.0, c = 0.0, x1 = 0.0, x2 = 0.0,t = 0.0,q = 0.0,real = 0.0,imaginary = 0.0;
//printf("请输入函数的系数。");
scanf("%lf%lf%lf", &a, &b, &c);
t = pow(b, 2) - (4 * a * c); //Δ在判断和赋值语句中经常使用,赋值在t中简化程序。
if (a == 0 && b == 0 && c != 0)
{
printf("Not An Equation");
}
if (a == 0 && b == 0 && c == 0)
{
printf("Zero Equation");
}
if (t >= 0 && a != 0) //Δ大于等于0,有连根。
{
q = sqrt(t);
x1 = (-b + q) / (2 * a);
x2 = (-b - q) / (2 * a);
if (x1 == x2) //两相等的实数根。
{
printf("%.2f", x1);
}
else if (x1 > x2)
{
printf("%.2f\n%.2f", x1, x2);
}
else
{
printf("%.2f\n%.2f", x2, x1); //使输出的两根大根在前,小根在后。
}
}
if (t < 0 && a != 0 && b != 0)
{
real = b / (2 * a);
imaginary = sqrt((-t) / (2 * a));
printf("%.2f+%.2fi\n%.2f+%.2fi", real, imaginary, real, imaginary);
}
else if (a == 0 && b != 0)
{
x1 = -c / b;
printf("%.2f", x1);
}
else if (t < 0 && b == 0 && a != 0) //实根为0.
{
t = fabs(t);
x1 = sqrt(t) / 2;
printf("0.00+%.2fi\n", x1);
printf("0.00-%.2fi", x1);
}
return 0;
}
实验总结:
看到这道题的时候我的心里是崩溃的,感想是无奈的,看不懂啊。我回忆高中的数学知识后认为我们并没有将一元二次方程和虚数一起讲过。在我那小的可怜的数学知识体系中,一元二次方程的解法和虚数是完全的两个部分,也许是我们高中数学老师压根没讲也有可能是我在三个月的暑假中把老师给我的知识统统都还给了老师。于是对这道题的琢磨完全就变成了对数学问题的挣扎。索性我们的度娘很可靠,让我基本的将实部和虚部弄懂了。对虚部的简单讲解。
- 善用网络和百度,有些高中并没有细讲一元二次方程求复数解。
- 这道题先做Raptor的流程图会更容易让我们写C语言一些,一元二次方程的求根的分类太多了,很容易造成我们的思维混乱,但先写Raptor却能理清我们的思维,让我们在写C时不必再过多的考虑数学问题。
- 写完Raptor的流程图我们就能发现其实C语言可以将这些写在一个if-else语句中,当然else if会很多,我们的思维只要不乱就能写完它。
- 难点并没有在C语言的运用上而是在高中数学上。
PTA提交列表:
(四)博客互评
对“怪人”的博客作业的小建议:
- 同学你对if-else语句的理解和解释对我很有帮助,直观清晰的把语句运行的逻辑表达了出来。
- 可以把PTA的每一个小题的提交列表附在每道题的总结后,这样会更加规整易看一些。而不是四道题都做完了,总结完了再把所有的题的提交列表给出,这样会让我们唯一的助教需要去找每一道题的提交的。
- 一些题的实验分析直接写没有问题还是有些显得内容不充实的,我认为可以加上自己对这道题易错点的分析来使的内容更充实的。
- 注释其实还可以用
//注释内容
的形式来写,这样在一些情况下可能会比/*注释内容*/
更加简洁易读。
对程晓磊的博客作业的建议:
- 和对“怪人”同学的建议一样,你的PTA的提交列表和老师给的模板不太一样。
- 你的博客里太多的无序列表了,几乎没一行之前都会跟着一个黑圆点,可以去掉一些无序列表来让博客看上去更美观一些。
- 可以在一些地方加一些分割线来让文章更易于观看。尤其是让我们唯一的助教更易于观看。
对“永恒破碎”同学的博客作业的一些小建议:
- 每道题后最好加上实验总结,我认为这部分是咱们这么大规模的作业中最精华,最对咱们有用的一个部分了,它可以让我们反思,复习我们前一周的代码的逻辑和更加的熟悉我们随学的主体代码。
- 学习总结可以再多一些,多一些自己的见解而不是已有的理论,丰富自己的博客内容。
- markdown的格式过少,markdown的功能非常强大,尤其是对我们这些过1024节的人来说。它插入代码的操作别其他的博客语言简单太多了。多一些博客格式没准就可以让咱们唯一的助教看的更加舒心。
没准就给咱们多一点分了你说对不对?
在我提交博客园作业前只有三位同学提交了博客园的作业,我也只能暂时只评论这三位同学的博客园了,如果我以后有机会会再加入其它同学的,谢谢以上3位同学更早的提交作业了。两位被我评论对不起了。程晓磊就算了