| 这个作业属于哪个班级 |
| ---- | ---- | ---- |
| 这个作业的地址 |
| 这个作业的目标 | 学习如何设计函数、C语言基本数据类型 |
| 姓名 | 骆锟宏 |
0. 展示PTA的题目
1. 本章学习总结:
1.1 函数的定义、调用、声明;
1.1.1函数的定义:
- 自定义函数的定义可以写在主函数之前,也可以写在主函数之后;
- 函数的功能往往是进行一个计算并得到这个计算的明确的结果,无返回的函数则是能起到一定的其他功能;
- 函数首部由函数类型、函数名和形式参数表组成,其中函数首部不能加分号,其与函数体一同构成完整的函数定义;
函数类型 函数名 (形式参数表)
{
函数实现的过程
}
- 特别地,形参表不能写成诸如
double a,b
这样的形式,每个形式变量前都要跟上它自己的数据类型。
1.1.2函数的调用:
- 形式:
函数名(实际参数表)
,注:实际参数表可以是常量、变量和表达式; - 对于实现计算功能的函数,函数调用经常使用以下两种情况:
- 赋值语句:
volume = cylinder(a,b);
- 输出语句:
printf("%d",cylinder(a,b));
- 函数在调用的过程中,传递的量其实不是变量,而只是变量所存放的那个数据,这叫做参数的传递;
- return语句只能传递一个值,而对于void类型的函数,则可以用
return;
来结束函数;
1.1.3函数的声明:
- 函数需要先定义后调用,而如果自定义函数放在主函数的后面,就需要将函数声明放在主函数的前面;用来说明函数的类型和参数的情况。
- 格式如下:
函数类型 函数名(参数表);
1.2 全局、局部变量,静态局部变量;
1.2.1全局变量:
- 全局变量的定义:全局变量一般定义在主函数之前,也就是第一个函数的前面。
- 全局变量可以帮助解决函数多结果返回的问题,但全局变量更多地用于多函数间的全局数据表示。
1.2.2局部变量:
- 局部变量一般定义在函数或者复合语句的开始处。
- 局部变量在函数调用中是单向传值,如果函数没有返回对应的计算结果给对应的局部变量,
那么局部变量的值不会改变。 - 不同函数范围内的局部变量,即使命名相同也不会相互影响。
1.2.3静态局部变量:
- 补充变量生存周期的概念,一般一个局部变量的生存周期只在它所定义的域也就是定义语句存在的大括号内。
- 由此我们引出静态局部变量的概念,那就是相对于局部变量来说,它的数据的生存周期会持续到整个程序的结束。
不会因为函数的每次调用而不断重置,而会在函数再次被调用时延续上一次调用时得到的值。 - 静态局部变量的实用:那就是静态局部变量如果带有虚幻结构的函数结合起来使用的话,会有很特殊的效果。
- 比如:可以通过不断传入一个递增的值和函数内一个初值为1的静态局部变量相乘,就可以得到阶乘的效果。
1.3 C语言数据类型及注意点;
-
数据的存储:(关于原码、反码和补码)
- 原码:符号位为1,其余各位表示数值的绝对值。
- 反码:符号位是1,其余各位对原码取反。
- 补码:反码加1.
- 符号位是0表示是正数,符号位是1表示是负数。
-
各类进制数的表示:
C语言中,十进制数由正、负号和阿拉伯数字0~9组成,但首位数字不能是0;
八进制由正负号和阿拉伯数字0~7组成,首位数字必须是0;
十六进制由正负号和阿拉伯数字09、英文字符af或者A~F组成,首位数字前必须有前缀0x或0X。 -
特别地注意转义字符这个特殊存在。
-
特殊的表示方法:
- 浮点数表示法:(实数的小数形式)实数由正号,负号阿拉伯数字0~9和小数点组成,必须有小数点,并且小数点前后至少一边要有数字。
- 科学计数法:(实数的指数形式)实数由正号、负号、数字和字母e(或E)组成,e是指数的标志;在e之前要有数据,e之后的指数只能是整数。
-
输入输出:
| 格式 | 含义 |
| ---- | ---- | ---- |
| %d | 以十进制的形式输入或输出一个整数 |
| %u | 以十进制的形式输入或输出一个无符号的整数 |
| %o | 以八进制的形式输入或输出一个整数 |
| %x | 以十六进制的形式输入或输出一个整数 |
- 自动类型转换:
水平方向的转换:如图所示,所有的char和short类型都会自动转换成int类型;所有的unsighed short会自动
转换成unsigned类型;long会自动转换成unsign long;float会自动转化成double。
垂直方向的转换:如果经过水平转换,参与运算的数据的类型仍然不相同的话,那这些数据将会再次自动转换成
级别最高的类型。 - 赋值类型的转换:
在做赋值运算的时候,系统自动把赋值号右侧表达式的类型自动转换成赋值号左侧变量的类型。
利用这条规则时,如果赋值号右侧表达式的类型比赋值号左侧变量的类型更高的话,运算精度会降低!!!
典例:
比如买u盘的题目中出现的误差问题,明明结果是14,但是对浮点数运算来说,结果会是13.9999999,而如果按这个规则
那得到的int类型的数据就是13而不是14,结果错误!!!所以要尽可能保持赋值符号两边的数据类型相同,遇到浮点数转整型
的问题,记得在浮点数表达式后面加上0.000000000000001。
另外要记得,不能直接比较两个浮点数相等!!!要说明两个浮点数相等的方法应该是两个浮点数的差小于0.000000001. - 买U盘代码如下:
#include<stdio.h>
int main()
{
//数据表达;
double money;
double price;
double amount;
int result;
//流程设计;
scanf("%lf %lf", &money, &price);
amount = (money / price) + 0.00000001 ;
//消除浮点误差,即即使是应该是14的值,在内存中会被视为13.9999999999999999
result = (int)amount;//强制类型转换,会直接将小数部分省去;
printf("%d", result);
return 0;
}
1.4 C运算符;
- 强制类型转换运算符
(类型名) 表达式;
先上图:给个整体概念~
- 算术运算符:
单目运算比双目运算级别更高:
单目:
| 自增 | 自减 | 正值 | 负值 |
| ---- | ---- | ---- | ---- | ---- |
| ++ | -- | + | - |
特别地,请注一下自增和自减运算的前缀形式和后缀形式对表达式值的不同结果。
以及该运算只针对变量,以及它具有改变变量值的附加作用。
双目:
加 | 减 | 乘 | 除 | 取余 |
---|---|---|---|---|
+ | - | * | / | % |
- 赋值运算符:
简单来说就是先计算赋值符号右边的表达式的结果,然后再把赋值符号右边的结果赋值给右边;
如果表达式左右数据类似不同会发生自动转换,具体规则见上面。
(另存在复合赋值运算,这个这里不赘述。) - 关系运算符:
要点主要是,该表式的结果式0或1.其中1代表真,0代表假。 - 逻辑运算符:
有单目运算符!
和双目运算符&&
和||
,即非运算且运算和或运算。
非运算对逻辑结果取反,且运算要求都为真才是真,或运算只需要一个为真就都为真。 - 条件运算符:
epx1 ? exp2 : exp3
该语句的运算顺序式,先对exp1进行计算,如果结果为真那就把exp2的值作为该表达式的值,否则就把exp3的值作为该表达式的值。 - 逗号运算符:
逗号表达式会从左往右计算各个表达式的值,然后把最后一个表达式的类型作为逗号表达式的类型,把最后一个表达式的值作为
逗号表达式的值。 - 位运算:(也就是进行二进制的运算)
- 除了取反运算时单目运算之外,其他都是双目运算;
- 操作数只能是整型或者字符型或者它们的变体;
- 操作数的位运算不会改变原操作数的值!
*** 除非你把位运算后的值赋值给原变量~~ *** - 各个运算的性质:取反运算就是把1变成0把0变成1;与运算就是都是1才是1否则就是0;或运算是有一个1就是1全为0才是0,
异或运算是相同才是0,不同都是1;所以要取一个数的反码就可以与1异或。 - 异或运算的特殊用法:
利用异或运算来交换两个数的值:a ^= b ^= a ^= b;
- a ^ a = 0;一个数和它本身异或的值为0;
- a ^ ~a = 二进制全一。(如果a以16位二进制表示则a为65535);一个数和它本身的取反求异或得到的结果就是把自己的每个位都变成 1 .
- ~(a ^ ~a ) = 0;在上面的结论取个反就全变0了。
- 移位运算:
a >> b就相当于将a的二进制右移b位,a << b就相当于将a的二进制左移b位。要求a和b都是整数,且b必须为正数,且不能超过机器字所表示的二进制数。- 在循环移位中,移入的位等于移出的位。
- 在逻辑运算中,移出的位丢失,移入的位取0.
- 在算术运算中,移出的位丢失,左移入的位取0,右移入的位取符号位,即负数取1,正数取0,也即最高位代表数据符号,保持不变。
- 一般左移一位相当于乘2,两位代表乘4,依次类推,右移反之。
- 其他运算:
- 长度运算符:sizeof(变量名/数据类型)
- ()括号用来改变运算顺序。
- []中括号用来表示数组下标。
- *和&,和指针运算有关,前者表示地址对应的内容,后者表示取用变量对应的地址。
- ->和. 用来表示结构分量。
1.5 全局变量和局部变量和静态局部变量的区分点:
- 首先从生存周期来看,全局变量和静态局部变量都能在整个程序当中存在,两个唯一的区别是,静态局部变量依然只能在被调用的函数中值有效,而
全局变量可以在多个函数中有效;而局部变量则是生存周期只在它所定义的域也就是定义语句存在的大括号内。 - 针对重名的变量,如果是在一个函数中,那么以整个函数内定义的变量优先,如果是在一个复合语句中,那么以这个语句内定义的变量优先。
1.6 学习体会;
- 在这周和老师的交流当中学习到了思维上比较高度的东西,最终发现,作为工科,其实最重要的是实践,只有不断实践,不断从实践中试错,不多面对一个一个bug冷静下来,
耐心用单步调试去找到其中的问题才是最重要的事情,而多动手,正是工科相别于理科最大的不同之处,工科的基础是实践,依靠实践不断来巩固理论,来积累经验,从实践中
找到问题,从实践中找到创新点,这才是工科人该做的事。这也是一种实事求是的精神的具体化,对应到当前的学习就是,老老实实多写代码,多去思考不要恐惧写代码;不要
害怕写代码;而这是有收获的,前几周苦脑的PTA刷题跟不上的问题在思维转变后得到了缓解和提升,这周以多写代码,多思考,敢于直面bug的态度去看问题就得到了进展。
总之,实事求是,不怕困难,艰苦奋斗,顽强进取!希望调整好指导思想后,俺的工科学得能越来越好!
这周开心的地方是,前面落下的部分题集,虽说没有全部补上来,但是都至少达到了2/3是有的!
那么这一周继续努力继续冲刺!
2. 小学生口算表达式自动生成系统:
2.1 模块流程图;
2.2 函数功能及全局变量介绍;
2.2.1 全局变量;
//下面是全局都要用到的变量:
int computerAnswer;//用来记录当下产生的表达式的正确的结果;
int countRight = 0;//用来记录用户一共答对了几题;
int countAll = 0;//用来记录具体模块内用户一共回答了几道题;
double trueRate;//用来计算正确率;
- 这些全局变量是应对如果一个人既想做一年级的,又想做二年级的,还想做三年级的题目的话;那这个全局变量可以统计它整体的正确率情况;
不过本次由于是采用多文件的编辑情况还不清楚跨文件的全局变量的定义;所以这个想法提出了概念,但是未能实行等待下次研究!
2.2.2 菜单相关函数;
void OperateMenu();//游戏操作界面
void GradeOneMenu();//一年级
void GradeTwoMenu();//二年级
void GradeThreeMenu();//三年级
- 这里的操作菜单函数就只有两级,用于提供选择的原始根菜单和选择后对应到不同年级的具体的操作菜单,大体格式和老师给的是差不多的;
但是有新加了一些地方,便是关于难度的选择和题目数量的选择上的布局有增设一些自己的文字,尽管比较简单。 - 代码如下:
- 这是根界面,选择界面的代码:
- 这是一年级的答题界面的代码;
- 这是二年级的答题界面的代码;
- 这是三年级的答题界面的代码;
2.2.3 与出题相关的函数;
int ProduceNumber(int level);//生成随机数
char ProduceSign(int level);//生成运算符号
void ProduceExpression(int level);//生成表达式的函数
void RightResponse(int numb);//比较灵活的人机互动
void WrongResponse(int numb);//比较灵活的人机互动
-
这次比较得意的方面就是与出题相关的函数的设计的这一方面吧,关键的点就在于,通过了形参的巧妙利用和级联的if-else结构
最终实现了不需要使用很多的函数也能达到相关的效果的样子;不过辩证的来看,如果将其整编为其他含数尤其是生成表达式的函数
这一块将一二三年级的不同的表达式拆开的话,说不定效果也不错(后期值得优化和尝试)。 -
后面的这两个灵活的人机互动的函数,是老师上课提到的一个概念就是利用随机数让计算机的回答更灵动一点,提高使用者的舒适度,
缓解审美疲劳。(这也是随机数加函数的比较经典的应用。)
2.3 运行结果截图,测试用例。
-
第二张图表达确实有进行正确率的输出!
-
一年级难度二的正常运行;
-
二年级难度一的正常运行;
-
二年级难度二的正常运行;
-
三年级难度一正常运行;
-
三年级难度二正常运行;
-
其他问题:视乎出现的数据有一些比较特殊的样子;可能是对于随机数的真正使它完全随机化的方法没有掌握透,这是从这次
大作业上反映的问题;将后期改进。
2.4 大作业中编程技巧总结
- 加入形参可以使生成随机数这个函数的使用范围更大;
- 对于需要利用循环的性质让语句不断执行,但是不需要依靠循环条件来跳出循环的,可以直接用 1 当条件;
------------恢复内容结束------------