期末时复习的一些心得(查漏补缺)
期末时复习的一些心得(查漏补缺)
期末心得day1_2022_6.14_0:18
C语言
-
!
的结合性比>
高,比如写出!(x>0||y>0)
等价的表达式,我们应该写成!(x>0)&&!(y>0)
而不是!x>0&&!y>0
。更多参考--->C语言运算符优先级 (biancheng.net) -
int *p[]
与int (*p)[]
的区别:-
int *p[]
是一个指针数组里面每一个元素都是指针,p可以理解为指向头指针的指针; -
一般
int (*p)[n]
是指p这个指针指向一个有n个整型的数组,需要注意的细节有:- p是一个指针,同时也是一个专门指向含有n个整型的数组,如
p[0]=array[n]
; p+1
的意思表示下一个指向n个整型的数组的指针,而不是单纯地在数值加一;p+i
和p[i]
可以表示同样的地址,但p+i+1
和p[i]+1
表示的完全不是同一个意思:p+i
的类型是int (*)[]
,可以理解成第i
个array[n]的首地址;所以p+i+1
是int (*)[]
之间的运算,指的是第i+1
个array[n]的首地址;p[i]
的类型是int []
,同样可以理解成第i
个数组,因此其代表的意思也是首地址;p[i]+1
是int []
之间的运算,所以就是指p[i][j]
的地址;
- p是一个指针,同时也是一个专门指向含有n个整型的数组,如
-
直接看实例:
-
判断小技巧总结:后缀的
[]
和前缀的*
在一定程度上是永远互相等价的,注意:下面的公式前只是表示类型而不是进行声明;\[int^*\;p[\;]\Rightarrow int\;p[\;][\;]\Rightarrow int^{**}\;p\\ int^{**}\;(p+i+1)=int^{*}\;(p+i+1)[\;]=int\;\&p[i+1][0]\\ int^*\;p[i]+1=int\;\&p[i][1] \]
-
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[]){
printf("Total is %d input params\n",argc);
for(int i=0;i<argc;i++){
printf("argv[%d]=%p|%s\n",i,argv[i],argv[i]);
printf("%d\n",strlen(argv[i]));
}
}
这个代码说明几点,argc
是输入参数的个数,程序名也算做自己的一个参数,argv
是一个二维数组(更贴切的来说是指向argv
是一个指向字符串的指针,由于字符串是数组,所以叫二维字符数组不过分吧)
期末心得day2_2022_6.15_0:26
C语言
-
fwrite
andfread
是互相对应的,他们的参数都是void *buffer, size_t size, size_t count, FILE * stream
其中buffer为接收数据的地址,size为一个单元的大小,count为单元个数,stream为文件流;具体看下面例子void demo2( ) { FILE *stream; float list[30] = { 12.3, 53.5, 45, 2, 1.02 }; // 以文本方式打开文件 if ( (stream = fopen( "fread_float.txt", "w+t" )) != NULL ) { // 如果读取无误 //"w+t"的意思是只允许写数据 int numwritten = fwrite( list, sizeof( float ), 5, stream ); printf( "Wrote %d items\n", numwritten ); fclose( stream ); } else { printf( "Problem opening the file\n" ); exit(0); } if ( (stream = fopen( "fread_float.txt", "r+t" )) != NULL ) { // 文件读取 //"r+t"的意思也是指读取数据 float j; for (int i = 0; i < 5; i++ ) { fread ( &j, sizeof(float), 1, stream); printf ( "%f\n", j ); } fclose( stream ); } else { printf( "File could not be opened\n" ); exit(0); } }
这里说一个小细节,我们读数据的时候是从二进制文件中读的,如果单从ASCII文件或文本文件中想读数据一般只读到字符串,大佬说过:
fread() 和 fwrite() 一般用于二进制文件的输入输出,ASCII文件还是不要考虑了
,所以我们打开写好的fread_float.txt
看看发现是这样的: -
在lexue的编程填空选择题测试中通常选项与题目不会放在同一页,此时一定要回去看题目的程序要求,答案起码会在这里出现一个,另外的则是根据程序猜测功能;
-
return和exit的不同
- return的对象是值,从函数中return,return后回到程序中继续进行;
- exit的对象是状态,从程序中exit,exit后回到操作系统中继续进行;
-
标识符不可以以数字开头,且不会含有标点符号;
-
打印图形题就尽可能打印图形进一个字符串,不要直接打印出来(直接打印出来我们修改不了)
- 给一个图形字符串初始化时顺便把输出也写了;
- 遵循李仲君老师说的方法,先打印简单的图形;
-
写成
num%i==0
总比!(num%i)
好,因为不知道哪天会把括号漏了,也让别人更容易看懂; -
scanf
后面使用gets
时记得先把用scanf
把\n
拿掉; -
写递归时注意格式,完全按照题目的条件写分支,否则很有可能因为深度出错;
大物
-
固体中纵波的波速(绳子),在固体中杨氏模量一般大于切变模量,所以纵波的波速一般大于横波;
\[u_l=\sqrt{\frac{E}{\rho}} \] -
固体中横波的波速(正是因为剪切形变的存在,横波才能在固体中传播)
\[u_t=\sqrt{\frac{G}{\rho}} \] -
波速由绳子的拉力和线密度决定
\[u=\sqrt{\frac{F}{\mu }}\\ F=\mu u^2\\ (where\;\mu是线密度,u是波速) \]- 这个式子说明了对于一根绳子他的拉力一定时,波速就一定了;
- \这个公式在求绳子小质元做简谐振动的弹性势能时会用到
-
在波的传递过程中,与弹簧振子的动能和弹性势能变化情况不同,波的能量(小质元的动能和弹性势能)是同相变化的;
-
波的相速度就是能量的传递速度;
-
当问到希望衍射中的级次变多时,直接想光栅方程
\[d\cdot sin\theta=k\cdot\lambda \]
期末心得day-4
C语言
-
在写字符串的冒泡排序时,有可能会遇到的问题
-
交换顺序的语句中,不应该创造临时指针来指向需要交换的字符串之一,而应该是创造一个字符数组来存下待交换字符串的每一个字符:
void store(char house[], char rice[]) { int len = strlen(rice); for (int i = 0; i < len; i++) { house[i] = rice[i]; } house[len] = '\0'; } void strsort(int cnt, char substr[cnt][100]) { for (int i = 0; i < cnt; i++) { for (int j = 0; j+i < cnt-1; j++) { if (substr[j+1][0] < substr[j][0]) { char buf[100]; store(buf, substr[j]); //上面这两句不能写成char *buf=substr[j] store(substr[j], substr[j+1]); store(substr[j+1], buf); } } } }
-
对于很长的判断条件时,比如
ch[i] >= 'a' && ch[i] <= 'z' || ch[i] >= 'A' && ch[i] <= 'Z'
有可能会警告:suggest parentheses around '&&' within '||'
- 理解:
||
(或)我们可以理解成两个条件并联;而&&
(并)就可以理解成两个条件串联,条件可以理解成一个二值的受控信号源; 其中A,B,C,D都可以理解成一个开关,只有短路和开路的形式,这里我们也可以看出在C语言中,程序的运行就相当于从左端过来一个电流,分别对负载起作用(判断开关状态),然后电流再决定通过与否,在并联中,我们看到电流都通过不了A,所以也不会去继续执行B并判断它的状态,再从边的图我们可以看出其实只要分别在&&
的元素的两侧加括号我们就很清晰其中的运行机制了。 - 改正:
(ch[i] >= 'a' && ch[i] <= 'z' )||( ch[i] >= 'A' && ch[i] <= 'Z')
- 理解:
-
期末心得day-3
短横杠是负数前面的那个负号的意思,距离暑假还有三天;
工数
- 对于求极数的导数时要观察有没有少项,要及时记得把改求和下方的n
期末心得day-2
C语言
-
写函数或者写递归时一定不能漏返回的语句,否则可能会导致本地编译器可以过且没报错但乐学上完全没通过而且会返回二进制文件返回给你,这代表了编译过程中链接步骤失败了,生成不了可执行程序,所以返回二进制文件;
-
还是要看准题目每一个细节,具体可以这样看
- 读入部分要求
- 处理部分要求
- 输出部分要求
然后再细分到函数或者功能实现上看要求,几乎所有要求都会有相应的用例会考到,这也就意味着如果我们以后无论是写文档还是写程序,都可以这样做:
- 写文档:写好自己想要实现的功能,每一个需求;
- 写程序:看着文档一步一步实现每一个需求,缺一不可;
-
莫名奇妙就写出了一种新的递归想法,感觉很开心
#include<stdio.h> int x2,y2; char isfind_point(int x1,int y1){ if(x1==x2&&y1==y2){ return 1; }else if(x1>x2||y1>y2){ return 0; } return isfind_point(x1+y1,y1)||isfind_point(x1,x1+y1); } int main(){ int x1,y1; scanf("%d,%d",&x1,&y1); scanf("%d,%d",&x2,&y2); if(isfind_point(x1,y1)){ printf("Yes.\n"); }else{ printf("No.\n"); } }
isfind_point(int x1,int y1)
是一个判断是否存在从(x1,y1)
到达(x2,y2)
的路径的函数,我觉得里面最精彩的地方莫过于return isfind_point(x1+y1,y1)||isfind_point(x1,x1+y1);
这句,这样如果展开的话就相当于一个树状的判断结构,又同时往两个方向去递归,我觉得这样非常精彩,因为以前都是只想过往一个函数去递归,从来没有想过同时递归两个函数,于是又有了对递归新的理解; -
上一道题的原题目如下:
题目描述: 对于任意一点(x, y),假设只有两种移动方式:(x, y) ->(x, x + y) ,(x, y) -> (x + y, y)。给定起点坐标(x1, y1),判断是否可以只通过上述移动方式到达终点坐标(x2, y2)。例如起点坐标为 (2, 10),终点坐标为(26, 12), 则 (2, 10)->(2, 12)->(14, 12)->(26, 12) 是有效的移动方式,可以从起点到达终点。 提示:判断能否从(x1,y1)通过限定的两种移动方式移动到(x2,y2),可以转化为判断能否从(x1,x1+y1)通过限定的两种移动方式移动到(x2,y2)以及能否从(x1+y1,y1)通过限定的两种移动方式移动到(x2,y2)。 输入: 第一行为起点坐标,第二行为终点坐标。 输出: 如果可以通过上述移动方式到达终点,输出Yes.,否则输出No. 样例输入: 2, 10 26, 12 样例输出: Yes.
当时自己没有看出来,看了网上的题解后发现可以这样做:
-
(2, 10)->(2, 12)->(14, 12)->(26, 12)
通过这个我们不难发现这就是辗转相除法,则我们只需要比较两者的最大公约数是否相等即可,这样可能更简单,下面是网上大佬(博客id:想念河的鱼)的代码:#include <stdio.h> #include <stdlib.h> #include <string.h> int n, k, ok = 0; int gcd (int m, int n) { return n == 0 ? m : gcd(n, m % n); } int main () { int x1, y1, x2, y2; scanf("%d,%d", &x1, &y1); scanf("%d,%d", &x2, &y2); if (gcd(x1, y1) == gcd(x2, y2) && x1 <= x2 && y1 <= y2) printf("Yes.\n"); else printf("No.\n"); }
-
开始刷一下选择题并进行查漏补缺
-
是否可以这样做
char *s4= "student";strcpy("s4", "hello4");
运行失败#include<stdio.h> #include<string.h> void demo1(){ char *s4= "student"; strcpy(s4, "hello4"); puts(s4); } int main(){ demo1(); return 0; }
-
原来C语言选择题是要选最错的那一个选项,有时在脑中编译其实过不了,但实际上编译器是允许溢出的存在且有时不会报错的,就比如
strcpy(“s4”, “hello4”)
把一个字符串拷贝到比它小的字符串中去,是可以运行的(所以选择题认为这是对的),但我们的知识告诉我们这是错的,因为会溢出; -
以下正确的说法是( ) A.如果形参与实参的类型不一致,以实参类型为准 B.如果函数值的类型与返回值类型不一致,以函数值类型为准 C.return后边的值不能为表达式 D.定义函数时,形参的类型说明可以放在函数体内
感觉这种题如果没有试过的话很难知道,再考试中去试的话可能就有时间上的问题了,所以还是多做题好;写了几行来验证一下
int f(int a){ printf("%u\n",sizeof(a)); char c='b'; return c; } void demo2(){ printf("%u|%u\n",f('a'),sizeof(f('a'))); printf("%u\n",sizeof(f('a'))); printf("%u",sizeof(f)); } int main(){ demo2(); return 0; }
4 98|4 4 1
-
A.
sizeof(a)=4
说明在函数中以形参的类型为准; -
B.以函数值的类型为准,其中还发现一个比较有意思的事情:
printf("%u",sizeof(f('a')));
并不会执行f('a')
,输出的结果就自然是4,而函数里面的printf
在这一步中就不会运行;- 而
printf("%u",sizeof(f))
输出的结果是1,这又在强调一个事情,就是f()
和f
是完全不同的,前者的类型是int,后者的类型其实是void ()
;
-
C.显然可以为表达式好吧;
-
D.形参不放在函数里面,放在()里面,但有人又来说这样也可以
int hhhhh(a,b) int a; int b; { return a+b; } void demo7(){ printf("%d",hhhhh(3,2)); } int main() { demo7(); return 0; }
就是说坚决不能放函数里面🃏,放里面的就不叫参数了,而是一个临时定义的局部变量;
-
-
在上一点中接着B选项的拓展:
void demo3(){ printf("%u\n",sizeof(void )); printf("%u\n",sizeof(void () )); printf("%u\n",sizeof(void * )); printf("%u\n",sizeof(void *() )); printf("%u\n",sizeof(void (*)() )); }
1 1 4 1 4
首先,对于void我们知道它是1,从结果分析我们知道了
void*
和void (*)()
都是地址,而void ()
就是普通函数名的类型,void *()
应该是另一种的指针函数的类型他们的大小都是1,看个例子就明白了:void demo2(); void* demo5(); void demo6() { printf("%u\n", sizeof(demo2() )); printf("%u\n", sizeof(demo2 )); printf("%u\n", sizeof(demo5() )); printf("%u\n", sizeof(demo5 )); } int main() { demo6(); return 0; }
1 1 4 1
-
void demo4() { double a[4] = {1, 2, 4, 66}; double *p = a; printf("%u\n", sizeof(a )); printf("%u\n", sizeof(double [4])); printf("%u\n", sizeof(p )); printf("%u\n", sizeof(double * )); printf("%.2lf|%u\n", a[3], sizeof(a[3])); printf("%.2lf|%u\n", p[3], sizeof(p[3])); }
在这个函数中,我们可以看出其实a的类型其实是
double [4]
,虽然我们经常把a当作一个数组的首地址去使用,但这也并不意味着a的类型就是double *
,上面的结果是:32 32 4 4 66.00|8 66.00|8
-
static int x
为静态存储持续变量,内部链接性,表示只能在本文件中使用;全局的常量的链接性默认为内部的,所以可以在不同文件间定义同名的常量,这并不会发生名称冲突(引用自百度知道),但也可以使用extern关键字来覆盖这种默认链接特性。- 静态区
- 数据段
- 只读数据段:
char *a="b_llsh_t";
- 读写数据段(包括已初始化的和未初始化的:初始化指赋初值)
- 全局变量
- 静态:
static int x
- 只读数据段:
- 代码段:二进制代码;
- 数据段
- 动态区
- 堆(heap):每malloc一次就会有随机分配一段要求大小的连续空间给你,所以只要malloc的单位够小那么变量的分布就都是随机的;
- 由程序员分配释放的内存空间;
- 若程序员不释放,程序结束时由系统回收;
- 堆是高地址扩展的数据结构(它的生长方向与内存的生长方向相同),是不连续的内存区域。
- 栈(stack):会存放函数的返回地址、参数和局部变量。声明在函数中一个局部变量
int b
;系统自动在栈中为b开辟空间。(通常地址都靠得比较近,因为是连续空间)- 由编译器自动分配释放的内存空间;
- 存取方式,先进后出,后来者居上,出的时候必须先拿掉最上面那个才能拿掉下面的;
- 在windows下栈是向底地址扩展的数据结构,是一块连续的内存区域(它的生长方向与内存的生长方向相反);
- 堆栈方面可以看看搜索方面是怎么应用的( BFS算法和DFS算法(含图解:简单易懂)_欦宇丶的博客-CSDN博客_dfs算法和bfs算法
- 堆(heap):每malloc一次就会有随机分配一段要求大小的连续空间给你,所以只要malloc的单位够小那么变量的分布就都是随机的;
- 上面参考了这篇文章->动态存储区、静态存储区、堆和栈的区别_妖小谦的博客-CSDN博客_静态存储区和动态存储区
- 静态区
-
结构体类型定义之后,编译系统并不会为其分配内存;只有定义的结构体变量在编译的阶段才分配空间;
-
终于有机会重新深入理解浮点数了;Online Binary-Decimal Converter (binaryconvert.com)这是一个超级棒的在线转换的网站,不早点知道太可惜了实在是;
-
库函数
fgets(s,n,f)
的功能是从文件f中读取长度为n-1的字符串存入指针s所指的内存,自己试一试;void demo10(){ char buf[10]; fgets(buf,7,stdin); puts(buf); } int main() { demo10(); return 0; }
-
二进制文件判断结束的方法是
feof
函数,feof
函数既可以判断二进制文件, 又可以判断文本文件; -
三十道选择题,尽量30min做完,有二十道基础知识,十道程序填空,大概有三个程序;
-
第一次见到循环链表的考题,比较有意思,可以总结的就是,看准生成链表的要求;