1.展示PTA总分
顺序结构:
分支结构:
2.本章学习总结
2.1. 学习内容总结
1.数据类型
C语言的3种基本数据类型是整型、字符型和浮点型:
- 整型 int 取值范围:-2147483648~+2147483648
- 短整型 short 取值范围:-32768~+32768
- 长整型 Long 取值范围:-2147483648~+2147483648
- 字符型 char 取值范围:-128~+127
- 单精度浮点型 float 取值范围:-/+3.4e38
- 双精度浮点型 double 取值范围:-/+1.7e308
根据MOOC的学习,在没有特殊需要的情况下,整型变量优先使用int,浮点型优先使用double,操作变量时,需要非常注意数据是否会溢出。
2.格式化输出与输入
- 格式化输出函数printf( );:该函数在系统文件stdio.h中声明,一般调用格式为“printf(格式控制字符串,输出参数1…输出参数2);”格式控制字符串用双引号括起来,表示输出的格式,输出参数是一些要输出的数据,可以是常量、变量或表达式。在输出格式控制说明中,可以加宽度限制词,制定数据的输出宽度,以达成一些题目的格式要求,或使输出内容变得整齐、清晰。
- 格式化输入函数scanf( );:该函数同样在系统文件stdio.h中声明,一般调用格式“scanf(格式控制字符串,输入参数1…输入参数2);”格式控制字符串用双引号括起来,表示输入的格式,输入参数必须和格式控制字符串中的格式控制说明相对应,并且类型、个数和位置要一一对应。在需要输入的变量前一定要加上“&”进行取地址运算,否则会出现变量无法读入的情况。
- 字符输入函数getchar( ):设ch是字符型变量,该函数一般调用格式为“ch = getchar( );”功能是从键盘输入一个字符,并赋值给变量ch,但是该函数只能读入一个字符。
- 字符输出函数putchar( ):设ch是字符型变量,该函数一般调用格式为“getchar(输出参数);”功能是输出输出参数的字符型变量或常量,该函数只能输出一个字符。
3.if-else语句
该语句的一般形式为
if ( 表达式 )
{
语句1;
}
else
{
语句2;
}
该语句用于实现分支结构,首先求解表达式,如果表达式的值为“真”,则执行语句1;如果表达式的值为“假”,则执行语句2,应用举例:c02-选择结构 7-2 判断偶数
4.for循环语句
使用该语句可以实现C语句的重复执行,一般形式为:
for ( 表达式1; 表达式2; 表达式3)
{
循环体语句;
}
for语句的执行流程是:先计算表达式1,再判断表达式2,若为“真”,则执行循环体语句,并接着计算表达式3,然后继续循环,若值为“假”,则结束循环,继续执行for的下一条语句。
- 表达式1:初值表达式,对循环变量赋初值,从而指定循环的起点。
- 表达式2:条件表达式,给出循环继续的条件。
- 表达式3:步长表达式,这个表达式是单次循环结束后需要执行的语句。
- 循环体语句:每一次循环需要执行的语句。
应用举例:2840大题库 实验2-3-1 -for 求1到100的和
5.常用的数学函数
- 平方根函数sqrt(x):计算(sqrt{x})。如是sqrt(4.0)的值为2.0。
- 绝对值函数fabs(x):计算|x|.如fabs(-3.56)的值为3.56。
- 幂函数pow(x,n):计算(x^n)。如pow(1.1,2)的值为1.21。
- 指数函数exp(x):计算(e^x)。如exp(2.3)的值为9.974182。
- 以e为底的对数函数log(x):计算(log{x})。如log(123.45)的值为4.815836。
- 根据老师的讲解,由于函数pow(x,n)内部是以循环的形式实现功能,因此如果在单个代码中多次调用该函数,效率会被明显降低,在下面的题目讲解中我会用实例来说明这个问题。
6.多分支结构
多分支结构有两种实现方式,分别是else-if语句和switch语句
else-if语句
一般形式为:
if (表达式1)
{
语句1;
}
…
else if (表达式n-1)
{
语句n - 1;
}
else
{
语句n;
}
它的执行流程为:首先求解表达式1,如果表达式1的值为“真”,则执行语句1,并结束整个if语句的执行,否则,求解表达式2……最后的else处理给出条件都不满足的情况,即表达式1,表达式2……表达式(n-1)的值都为“假”时,执行语句n。
switch语句
一般形式为:
switch (表达式)
{
case 常量表达式1:语句段1; break;
…
case 常量表达式n:语句段n;break;
default:语句段n+1;break;
}
该语句的执行流程为:首先求解表达式,如果表达式的值与某个常量表达式的值相等,则执行该常量表达式后的相应语句段,如果表达式的值与任何一个常量表达式都不相等,则执行default后的语句段,最后执行break;语句,跳出switch语句。应用举例c02-选择结构 7-9 成绩转换
- switch语句中的常量表达式必须是常量。
else-if语句和switch语句的不同点
- switch语句实现的是一个跳转作用,直接跳转到符合条件的代码去执行,仅进行1次判断,在else if语句中,每当使用一个if,就会进行一次判断,在多分支结构中,判断次数有可能不止一次;
- switch语句中,判断对象是表达式,起到判断作用的是常量表达式,如果该表达式与某个常数表达式相等就进行跳转,else if的判断对象也是表达式,没有利用常量表达式进行跳转的功能;
- 单个switch语句就能实现多分支结构,但如果只有一个if else,最多只能实现二分支结构,必须多个else if语句配合;
- switch语句需要配合break语句实现多分支结构,否则有可能出现多个分支都被执行的情况,合理运用这个特点可以作为一种技巧,而else-if的判断则是非此即彼的。
7.关于实现四舍五入的讨论
在完成PTA平台上的习题时,我们会遇到题目要求实现四舍五入的情况,这个时候有两种方法来实现。
法一:调用数学函数round(x)
应用举例:
运行结果:
根据我这段简单的代码和调试结果,我们可以看出数学函数“round( )”的用法:当变量是浮点型时,该函数将返回小数对整数部分的四舍五入的值。
法二:利用宽度限制词
应用举例:
运行结果:
根据这段代码以及运行结果,我们可以看出,在限制浮点数输出的位数时,会自动实现四舍五入。
用强制类型转换实现四舍五入的思考
想法来源:
让我们来验证一下晓凇学长提出的想法吧!
运行结果:
很遗憾,目前使用强制类型转换实现四舍五入的方法“破产”了,但是,如果我非要用这个方式来实现四舍五入的话,能不能强行实现呢?
强行实现代码:
运行结果:
我们成功地强行实现了四舍五入,你看懂这段代码了吗?我们把一个浮点数变量乘上了10,然后加上了5,再把它除以10,此时int类型的num2和num4会将结果的小数部分舍去,这时,如果小数部分是0.5~0.9的数字一定会进位,而小数部分是0.1~0.4的数字即使乘上10,加上5,也一定不会进位,当舍去小数部分时,除非进位了,否则我们加上的5和原有的小数部分都会被舍去,这样就强行用强制类型转换实现了四舍五入。
- 这只是一个想法,我们有更好的方法来实现四舍五入,因此这个想法只作为一个分享。
- 强制类型转换的代码格式是:(类型)<值>;在翁恺老师的MOOC中有对这个知识点的详细介绍,因此不展开详细叙述。
2.2 本章学习体会
学习感受
通过了这两周的学习,我深刻理解到了老师说的“C语言不可能看会、听会,只能练会!”在我看MOOC和听林老师的讲解时,有时候会觉得我已经能完全理解老师们讲授的知识点,但是上机写代码时还是会漏洞百出。由此可见,想要学好C语言,投入时间,努力练习是必不可少的,必须有足够多的代码量,才能有一个扎实的编程基础,这一定是需要一个过程的。在这个过程中,将会遇到各种奇形怪状的问题,碰到很多Bug和我们躲猫猫,有时候明明实现的题目的要求,但还是有很多测试点无法顺利通过,不得不花一整天甚至好几天进行调试。但是,当把一段代码好不容易熬熬熬熬出来之后,那种满足感是多么的强烈啊!这两周只是一个开始,希望在接下来的学习,我能够拥有更浓烈的求知欲去锻炼编写代码的能力。
教学建议
希望在接下来的课程中,能够接触到更多诸如随机数的产生等拓展知识,增加知识储备。
代码量统计
- 仅记录PTA答案正确的代码量,不包括修改的代码以及单行的“{ }”。
3.PTA实验作业
3.1 判断一个三位数是否为水仙花数
3.1.1数据处理
- 数据表达:
- int number1,存储输入的三位整数;
- int number2,备份number1,为判断是否是水仙花数做准备;
- int position = 0,存放number1的各个位的数字;
- int i = 0,控制循环开始、结束的变量;
- int sum = 0,进行记录水仙花数计算的结果,用于与number2比较。
- 数据处理:
一个输入语句,一个for循环结构,两个if-else二分支结构,三个输出出口,具体流程在代码截图中有详细说明。
3.1.2 代码截图
3.1.3 PTA提交列表及说明
Q:第一次提交时,由于for循环语句中的“循环进行的条件”语句没有书写正确,导致运行超时;
A:将该行代码修改为“for (i = 0; i < 3; i++)“就可以顺利结束循环。
3.1.4 本题可扩展功能
功能一:实现判断一个任意位数的整数是否是水仙花数功能的函数,题目连接2840大题库 实验5-9 使用函数输出水仙花数
思考:如果我们需要判断任意位数的整数是否是水仙花数,代码该怎么实现?
代码实现:
功能二:找出n位数中的所有水仙花数,题目连接2840大题库 实验4-2-5-嵌套循环 水仙花数
思考:如果我们需要一次性找出n位数中的所有水仙花数,代码该怎么实现?
代码实现:
但是,如果直接将这段代码提交了,我们会看到这样的审核结果:
为什么会出现运行超时的情况呢?这就是林老师所说的数学函数pow(x,n)的运行效率问题,由于函数pow(x,n)内部是通过循环实现的,当多个循环嵌套时,运行时间将被大幅度延长。这个问题的解法是——我们自己写一个mypow(x,n)函数:
然后用我们自己的函数替换掉所有的pow(x,n)函数:
我们可以很明显地看到,程序运行效率被大幅度地提升了。
3.2 计算天数
3.2.1数据处理
- 数据表达:
- int year,存储输入的年份;
- int month,存储输入的月份;
- int day,存储输入的日期;
- int sum = 0,用于存放总天数。
- 数据处理
一个输入语句,一个switch多分支结构,一个嵌套的if判断语句,一个输出出口,具体流程在代码截图中有详细说明。
3.2.2 代码截图
法一:
3.2.3 本题可扩展功能
我一共有4种代码写法来完成这道题目。
法二:switch多分支结构改进
思考:如果是方法一的代码,我们需要提前算好每个月份的第一天是这一年的第几天,这么写代码比较繁琐、费时,我们能不能让计算机帮我们做这项工作呢?
代码实现:
你看懂这段代码了吗?这段代码利用了switch语句的特性:如果没有遇到break语句,程序会继续执行下一个case。当程序在switch结构发生跳转后,会自动将接下来的所有case统统执行一遍,这样就实现了天数的自动累加。代码的其他部分不需要做改动。
法三:else-if多分支结构
思考:我们可不可以用另一种多分支结构来写这段代码呢?
代码实现:
只需要将法一的switch结构替换成这段else-if结构的代码,也可以实现计算天数,从这段代码中,我们不难看出else-if多分支结构的判断次数很有可能多于switch结构的判断次数。
法四:数组
如果你有提前看书或者看MOOC,你就会知道数组也很适合用来解决这道题目。
代码实现:
利用数组,我们也可以实现类似方法二的效果,而且这种方法的代码量是相对较少的。
3.2.4 PTA提交列表及说明
A:我把以上四种代码写法都进行了提交,这四种方法都能够完成这道题目。
3.3 于龙遇见日期,又哭了!
3.3.1数据处理
- 数据表达:
- int date[3],用数组存放输入的三个数字;
- int i1 = 0,控制循环开始、结束的变量;
- char c1, c2,存放三个数字中间的连接符号,确保正确读入数据;
- int oneYear[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 },该数组的单元初始化数据为对应月份的天数,判断日期的合法性时要用
- int year = 0,存储代表年份的数字;
- int day = 0,存储代表日期的数字;
- int month = 0,存储代表月份的数字;
- int idx1 = 0,用于记录date数组中最大值的下标;
- int idx2 = 0,在第二次遍历中,配合循环结构完成数组单元的位移;
- int judge = 0,作为判断日期合法性的flag。
- 数据处理:
一个输入语句,两个for循环结构,三个if-else嵌套判断结构,三个输出出口,具体流程在代码截图中有详细说明。
3.3.2 代码截图
3.3.3 本题可扩展功能
由于这道题的代码功能比较全面,因此这个部分我主要谈谈我对这道题的解析与看法。我猜,部分同学写这道题,可能写着写着就和于龙一起哭了起来,因为无论怎么改代码,总有那么几个测试点无法通过。正如林老师所说,这道题的思路其实并不难想,我认为难点在于两大方面:一、能否找出输入数据的所有可能性,并写出相应的代码分别操作?二、能否实现诸如闰年判断、日期合法性判断等判断机制?第一个方面,我举个例子,如果我们一上来就把输入的三个数按排序,那么这种可能性就有可能被忽略,这时无论你怎么改后面的代码,一定会有某几个测试点过不去,很多同学面临的很可能就是这个问题,因此在写这道题之前,我们一定要想清楚,有多少种可能性是需要我们考虑的?我们的每一部分代码能否对这些可能性进行相对应的操作?第二个方面,如果我们把闰年判断、日期合法性判断等机制拿出来单独出题,也可以出几道基础题来,把思路逆转过来,这道题也可以当作好几道基础题的拼接整合,在这个时候,代码各个部分的协调显得尤为重要,这个方面在我们写到“圆形体体积计算器”这种题目,甚至是链表,就会更加体现不同部分代码的协调问题。但是,如果这些拆分出来的小部分你无法顺利完成的话,那么你可能就需要思考:我对知识点是不是不是很熟悉?代码量是不是不太够啊?如果是这样的话,我们还有一个2840大题库可以拿来进行练习。我还有一个想法,如果在出题时,老师把题干的7组数据删掉,这道题的难度还能再提升一个档次。除非能一次过关,否则我们就需要进行调试来寻找错误,在测试数据有限的情况下,我们能否自己造成一些特例去把那些在“躲猫猫”的可能性都找出来?我认为,这是我们应该要有意识去培养的一种能力。
3.3.4 PTA提交列表及说明
A:在小测结束后,我对我的代码进行了优化,删除了一些不必要的变量,简化了代码。
4.阅读代码
题目来源 zoj 1205 Martian Addition
题干:
题意概括:利用代码模拟实现100位数字以内的20进制加法。
亮点分析:
- 这段代码的作者通过字符串来解答这道题目,将题目分解成5个函数来解决问题,分别是int CharToVal(char c)和void CopyNum(char* dest, const char* line)两个函数实现字符串的读入,函数void Add( )实现20进制的加法,函数void PrintResult( )实现结果的输出以及主函数int main( ),通过对题目的分解,使题目的功能更容易实现,也利于读者阅读学习;
- 使用了函数memset( ),高效地实现了对数组进行填充、清零的操作;
- 灵活运用字符串查找函数strchr( )和字符串长度函数strlen( ),配合for循环完成了字符串的准确输入,同时在void CopyNum(char* dest, const char* line)函数中使用了“const char* line”的写法,保证了指针line不能指向其他变量,保证了指针line所指向的变量的准确提取;
- 在void Add( )函数中,看似遥不可及的20进制运算,作者利用了for循环语句和求余运算实现了加法运算、进位的功能,达成了20进制加法的目的;
- 在int main( )主函数中,while的条件“scanf("%s",line)!=EOF && strcmp(line,"END")!=0”在读入字符串的同时一并检查是否开始、继续循环,我是第一次看到这种写法,不仅输入了数据,更是直接参与了条件运算;
- 在void PrintResult( )输出函数中,作者没有直接输出,而是利用数组的嵌套,实现了对照20进制数位表NUMS直接打印结果的功能。