C博客作业01——分支,顺序结构
C博客作业01——分支,顺序结构
0.展示PTA总分
1本章学习内容
1.1学习内容总结
1)格式化输出函数printf(),scanf()。
- 它是什么?
对于初学者而言,一开始了解接触它们,只是被硬性的告诉,一个是用来输出,一个是用来输入,但是如果提前学习,就会知道,这是我们后面要学的函数部分,很多东西,学到后面,便明白,原来一切都是有始有终。
- 函数printf()与函数scanf()是系统提供的库函数,在系统文件stdio.h中声明,所以在源程序开始时,要使用编译器预处理命令#include<stdio.h>。
-
printf()使用时的注意事项
-
在printf()函数中,所有的普通字符都会被原样输出,输出参数必须和格式控制字符串中的格式控制说明相对应,并且注意它们的类型,个数和位置要一一对应,如果是整型,用%d,浮点型用%f。
-
在输出格式控制说明中,可以加宽度限定词,指定数据的输出宽度。例如%md,指定了数据的输出宽度为m(包括符号位)。若数据的实际位数(含符号位)小于m,则左端补空格;若大于m,则按是实际位数输出,如果写成%0md,则是在左端补零。实型数据的输出格式控制说明%m.nf,指定了输出浮点型数据时保留n为小数,且输出宽度是m,(包括符号位和小数点)。当然,如果m<0,则是在右端补空格。
-
-
scanf()使用时的注意事项
-
相比较与printf()函数,scanf()函数的注意事项更容易让我们在考试中忘记。
第一点,我认为对于scanf()函数最为重要的是&符号,&符号是取地址的意思。我做题的很多时候,总是会忘记这个,虽然通过编译器可以查出来,但是毕竟浪费了我的时间,这里有梅梅学长的一个比喻:可以看做一个送货上门的快递小哥,传递的值就像快递,所以快递小哥需要你传入变量的地址,才能把值给变量,不然只有个变量的名字,快递送不到手上,而这个时候,我就需要&来传入这个变量的地址。很多时候,如果在我打下scanf的那个瞬间,明白它的本质,我也就不会忘记“&”,哈哈哈,偷偷在心里嘀咕,说是这么说,下次可能又忘记,哈哈哈。
第二点,在一般情况下,scanf(“%d%d”,&value1,&value2);在终端打出一个数字加空格加一个数字是可以的,但是当遇到%c时,要特别注意,因为%c这个家伙,会偷偷把空格吃掉,所以正确的写法应该是scanf(“%d %c %d”,&value1,&op,&value2);
小思考:为啥以%d%d输出和%d %d输入的方式一样?
- scanf里面,默认是用空白制表符把输入的数据隔开的,也就是,你的数据和数据之间, 都要有空白制表符分隔,空格, 回车, tab这些都是空白制表符,其实输入的两个%d之间是默认要有一个空格来分隔的,所以和%d空格%d输入的方式一样。
2)二分支结构和多分支结构。
-
二分支结构的形式有两种:if-else语句和省略的if语句
-
(a)省略的if语句
- if(表达式) { 语句1; if(表达式) { 语句2; } return 0;
- 执行流程:先求解表达式,如果表达式的值为“真”,就执行语句1;否则,则跳到第二个if语句或两个if语句都不执行。
-
(b)if-else语句
- if(表达式) { 语句1; else { 语句2; } return 0;
- 执行流程:先求解表达式,如果表达式的值为“真”,就执行语句1;否则,就执行语句2。语句1和语句2总要执行一个,但是不会都执行。简单案例:[7-2判断偶数](https://pintia.cn/problem-sets/1174498022735187968/problems/1174498260338315265)
-
多分支结构else-if
- if(表达式1) { 语句1; else if(表达式2) { 语句2; } ...... else { 语句n; } return 0;
-
执行流程:首先求解表达式1,如果表达式1的值为“真”,则进行语句1,并结束整个if语句的执行,否则,求解表达式2......最后的else处理给出条件都不满足的情况,即表达式1,表达式2......表达式n-1的值都为“假”时,执行语句n。简单案例:7-5A除B
-
比较上述三种结构的不同点。
-
对于省略的if语句,它虽然比较明了,但是每个分支都要判断,执行效率比较低。而if-else语句虽然不需要每个分支都判断,但是如果有多层嵌套时,容易出错。而多分支结构else-if能够找到满足条件执行相应分支,无需每个分支都判断,效率高。
3)switcth -case结构
- switch(表达式) { case常量表达式1: 语句段1; break; case常量表达式2: 语句段2; break; ...... default: 语句段n+1; break; } return 0;
- 执行流程:首先求解表达式,如果表达式的值与某个常量表达式的值相等,则执行该常量表达式后的相应语句段,如果表达式的值与任何一个常量表达式的值都不相等,则执行default后的语句段,最后执行break语句,跳出switch语句。
-
-
补充:break语句与continue语句的应用
-
(a)break语句和continue语句一般用于循环语句中,不过,break语句还可以用在switch-case结构中。在循环语句中,一旦执行了break语句,循环提前结束,不再执行循环体中位于其后的其它语句。break语句应该和if语句配合使用,即条件满足时,才执行break跳出循环;否则,若break无条件执行,都会结束循环,有时需要区分循环的结束条件。具体案例将会在我的知识盲区中给出。
-
(b)continue语句的作用时跳过循环体中continue后面的语句,继续下一次的循环。
4)生成随机数。
-
-
rand()0-0x7ff的随机数
-
简单案例:
-
由于rand()函数只能随机生成一组数,再次运行以后数是不变的,于是根据需要,我们引进srand()函数。
-
-
srand(time(0))生成不同的随机数
-
简单案例:
5)for循环。
-
执行流程:先计算表达式1,在判断表达式2,若值为“真”,则执行循环语句,并接着计算表达式3,然后继续循环;若“值”为假,则结束循环,继续执行for的下条语句。其中表达式1只在进入循环前执行一次,并且表达式之间用分号隔开。
-
1.2本章学习体会
- 我的学习感受
学习这两章的内容,是相对轻松的,一方面是在暑假看过慕课视频,另一方面,是其本身内容相对简单。但是其实我在总结上面的知识点的过程,其实就隐隐发现,我对一部分内容的掌握并没有那么顺利。比如,对于随机数应用,因为在刷pta的过程中,还没有运用到srand()函数,所以自己打这部分的代码时,就显得很速度很慢,并且在上课时,对1为真,0为假的理解也并没有那么充分,所以实践出真知吧,对事情的真正掌握就是通过你会不会熟练掌握。<
- 我的知识盲区和错误
- Q1:for循环中,循环语句只有一条语句,多条语句要用复合语句表示。
- 错误案例:
对于这道题,其实对别人来说很简单,但是对我这种脑袋比较大条的人,是不容易看出来的,它最后的结果是有语法错误,不能通过编译。之所以把它拿出来,更多的是让自己明白,作为一个合格的程序员,会打代码只是基础中的基础,而学会自己发现其中的错误是更为可贵的东西,对于这些小之又小,且易被忽略的细节,尽管只有一条语句时,也应该加上{},这提醒我的就是代码规范问题,代码规范会让自己以后的路更好走。
- Q2:对于break缺少情况的判断
- 简单案例:
- 简单案例:
这个是我国庆一起玩的小伙伴来问我的一道题目,在没有大佬指点的前提下,看到它我是懵逼的,咋会出现一个复合语句,然后,我就懂了,原来是break这个家伙搞的鬼,哈哈哈,我觉得果然题目还是多练比较好,让自己印象深刻。但其实也知道这还是我不熟练的情况,飘过飘过~~
- Q3:对于1为真,0为假的理解错误</span
在条件判断中,关系运算是一种比较运算,比如x>5,对这两个操作数进行比较,既然是一种比较,运算的结果只有真和假,而计算机只认识1和0,所以1表示真,0表示假,但是我一开始的不理解,所以对于if(x%2)这样的式子表示懵逼,到底什么时候进入if语句。
- 计算这周的代码量
第四周 | 第五周 | |
---|---|---|
代码量 | 41 | 1140 |
2.PTA实验作业
2.1 c02-选择结构7-10 计算天数
题目:输入在一行中按照格式“yyyy/mm/dd”(即“年/月/日”)给出日期。注意:闰年的判别条件是该年年份能被4整除但不能被100整除、或者能被400整除。闰年的2月有29天。在一行输出日期是该年中的第几天。
2.1.1数据处理
-
数据表达
- 1.通过定义整型变量year,month,day来存储年月日。
2.通过定义整型cnt1,cnt2,i(for循环)来算大月,小月的次数(除二月)。
3.定义整型time算出输出日期是该年中的第几天。
- 1.通过定义整型变量year,month,day来存储年月日。
-
数据处理
-
1.使用if语句加上题目给出的条件,判断该年是否为闰年,若为闰年,则在如果月份大于2的情况下,二月的天数为29天,否则为28天,二月的天数特地拿出来另外判断。
2.通过for循环,从最小的一月开始,逐渐变大,对大月,小月经过的次数进行计算。
3.最后通过小月出现的次数乘30加上大月出现的次数乘31加上二月的天数加上日,即为该日期是该年中的第几天。2.1.2代码截图
2.1.3本题可扩展功能
-
使用switch-case语句对该程序进行简化
-
我觉得使用第二种方法写出来的代码给人的感觉更好,更加高大上,首先,可读性比第一种来的好,思路清晰明了,不像第一种那么冗长,需要花费一定的时间去理解。其次,很好的利用到中间变量b的作用,其实对于以后遇到的题目,以及我现阶段遇到的题目,中间变量都是一个很好用工具。最后,我发现对于一些简单的问题,我写出来的代码却不如从前来的简便快速,因为总想利用现在学的东西,却发现一些条件是不允许的,或条件不够,就导致我的时间大把流逝,说是浪费其实并不是,也是自己在对于一些语法上的探索,会更加印象深刻。
2.1.4PTA提交列表及说明
Q1:这里的编译错误有一部分原因是scanf和scanf_s的原因虽然已经教过我们把scanf_s改成scanf,但是我嫌每次都该太麻烦了,而且我有点强迫症,看到下面的那条绿线警告,就有点甩锅而去冲动,所以每次直接复制过来,也没想那么多,结果在pta里不允许。
Q2:在计算天数时,没有意识到最后一个月其实是不能加进去的,因为这个月还没完,导致多加了一个月的时间。
A2:通过断点处理,设置添加监控,或把printf打印出来,就可以看到具体的数据,以及程序行走的流程。
Q3:由于时间久远,我记忆不好,所以,,,下面的错误请自己脑补,哈哈哈。
2.2 实验2-4-4 求阶乘序列前N项和
题目:计算序列 1!+2!+3!+⋯ 的前N项之和。
2.2.1数据处理
-
数据表达
- 1.定义N,存储输入的值;
2.定义i,j用于for循环;
3.定义sum求和;
数据处理
- 1.使用for循环对数求阶乘和对数进行累加。
2.2.2代码截图
2.2.3本题可扩展功能
- 1.定义N,存储输入的值;
-
求阶乘时,可以通过自定义函数,使结构更加清晰明了
-
代码截图:
-
在真正的编程工作中,我们需要完成的代码将非常大,所以将代码合理的分为不同的区块是很有必要的,每一个区块具有相对独立的功能,并为其它程序提供对外调用的参数和返回值,这样由多个区域组成的程序才会让程序阅读者更方便的理解程序设计的理念,并可以通过函数让功能被封装起来,使得一个功能可以在不同的情况下被其它功能调用。
-
调用函数,其实在我理解就是把我们想重复做的某一件事情抓出来,在外面完成,和我们在main()函数里写的其实差不多,多的是添加了一个返回类型,函数名,和参数表,然后把我们想让它重复做的事情放进去,就会让我们的main()函数显得更加简洁清晰。
-
-
变式:特殊a串数列求和
题目:给定两个均不超过9的正整数a和n,要求编写程序求a+aa+aaa++⋯+aa⋯a(n个a)之和。
- 代码截图:
这题的思路其实和上面的差不多,但是我其实想强调的是,因地制宜,我们以后会学到更多的东西,不一定每次都要用到函数,如果代码量在可以接受的范围内,我们可以直接在main()函数里直接进行。
2.1.4PTA提交列表及说明
Q1:当时做的时候,其实可能并没有意识到要用到for两层循环做这件事情,当时其实对使用多层for语句并不熟悉,所以做这题时就很废时间。
A1:后来我实在不知道咋办,就去问了问度娘,结果度娘上的大佬告诉我真相,哈哈哈。
- 在这里,对于多层for循环,我有个我觉得很好的例题,在慕课上也有出现过,刚开始看的时候,我其实对它的理解并不多。有兴趣的小伙伴点这:写出十元兑换成1元2元5元的不同方案并显示有多少种,当然,这道题还可以变式,让他输出一种方案,这时就可以利用break语句的特点。想知道的小伙伴请回看视频,哈哈哈。
3.2 c03-单循环结构 7-10 jmu-c- 二进制转十进制
3.2.1 数据处理
- 数据表达
- 1.定义一个足够数量的数组来存放每一位数。
2.定义i,j在使用for循环的时候用到。
3.定义fact,sum求二进制对应十进制求和。
4.定义cnt算有几位数
- 1.定义一个足够数量的数组来存放每一位数。
- 数据处理
- 1.使用fact = 1.0 * a[j] * pow(2, cnt - 1);sum += fact;求十进制的数。
2.由于不知道究竟要输入多少位数,所以用for (i = 0; scanf("%1d", &a[i])!=EOF; i++){}来存入每一位数,具体说明在下面会解释的。
3.使用for循环对数组进行遍历。3.2.2代码截图
- 1.使用fact = 1.0 * a[j] * pow(2, cnt - 1);sum += fact;求十进制的数。
- 知识点解释
- 有人会对%1d产生好奇,它表示我们输入一个数,只读取它的第一位,效果如下:
- 那么,对于这道题的难点,就是如何把一个数一个数分开,我开始按照从前的老办法写题,代码如下:
用这种方法做出来的,就是N的取值范围已经远远超过int的范围,所以应该把N转为double类型,但是如果把N转为double类型,那么接下来的对N取余全都无效。
- 所以,第一种方法,利用数组
1.这个结构是对输入一个五位数,将它的每一位存入数组对应单元中。如果对于如何把每一位数取出来的问题解决了,接下来,还有一个问题,就是题目并没有给出到底会输入多少位数的数,所以我们可以弄一个大空间的数组,然后对它进行遍历即可,但在这里,经梅梅学长的指点,我学会了另一种做法,具体代码如下:
在这里,我就不过多解释,因为我还小T-T,不敢在这里班门弄斧,怕祸害祖国的花朵,但是梅梅学长跟我讲的很仔细,只是我比较怂,哈哈哈,总之,它的结果大致就是把我们输入的数一个一个存入数组。
对于EOF不懂的小伙伴,请看这里:[EOF](https://baike.sogou.com/v851938.htm?fromTitle=EOF)
- 第二种方法:利用getchar()
- 代码如下:
Q:这个是我的小舍友写的代码,不过其实我对与第十五行的式子依然不懂它是怎么来的,懂得大佬请赐教,哈哈哈。
-
第三种方法:利用函数工具
-
代码如下:
-
知识点解释
-
对ch[i] - '0'的解释:字符的1如果要得到整型的1, 那么需要做一个简单的处理,‘1'的ascii码为49,'0'的是48,所以'1'-'0'的值是1,如果要得到其他数字同理。
-
对于strlen()这个函数,这个函数的作用是得到字符串的长度, 只是在这里的作用是得到数的长度。比如字符串ch是abcde的话, 那么size的值就是5。
2.3.4PTA提交列表及说明
-
Q1:其实因为一开始对二进制转换为十进制不熟悉,所以我请教了我的小伙伴,可以把所以都看成1,然后减去0出现的那部分,对于所以都是1则可以通过等比数列求和,但是到最后发现写出来的结果太复杂,于是换成了上面的代码,却又出现了对double类型不能取余的编译问题。
3阅读代码
题目源:ACM
- 代码功能:这道题其实更多的是偏重一道数学题,据说有一个具有超能力的人(这种人天天都不干正经事的嘛?!)他最多可以进行3级滑翔,这意味着在他降落在地面之前,他有两次机会调整和执行另一次滑翔。如上图所示,他从Building 1的顶部滑翔,进行1级或2级滑翔,降落在B点。此外,Building 1和B点之间还有Building 2。他必须避免撞上它。
所以这道题的关键,其实应该是看好题目,根据题目给出的已知公式和条件,并且要理解好每次降落的临界点设置条件。这个答案的整体结构是自定义了一个函数,还有一个main()函数,并且你会发现,其实函数返回值以及条件判断等都是很缜密的,嘿嘿,其实这段代码马马虎虎是可以看出来它在干嘛的,不过要批评的是(假装严肃),它的大括号没有各自占一行,其他的都很好。
在最后,要十分谢谢帮助我的梅梅学长以及学姐们和我亲爱的小伙伴们,哈哈哈!!