C语言小笔记—>复习(刷题)记录
快到一年一度的蓝桥杯选拔了, 可惜学校不开设python组 /(ㄒoㄒ)/~~😟
于是花了一个多星期来复习C语言顺便刷了点题,整理了一点笔记,记录一下。
原码、反码、补码和移码
计算机处理信息都是二进制形式表示的,下面以整型数据格式举栗子👇
不妨设每个整数在内存中占用两个字节存储,最左边的一位(最高位)是符号位,0代表正数,1代表负数, 整数1
的±
原码、反码和补码如下👇
栗子(demo)
- 假设每个整数在内存中占用两个字节存储(16位)
- 正数部分->
+1
- 原码:
00000000 00000001
- 反码:
00000000 00000001
- 补码:
00000000 00000001
- 移码:
10000000 00000001
- 原码:
- 负数部分->
-1
- 原码:
10000000 00000001
- 反码:
11111111 11111110
- 补码:
11111111 11111111
- 移码:
01111111 11111111
- 原码:
总结
- 原码: 最高位(最左边)是符号位,0->正,1->负。
- 正数: 原码、反码和补码都一样
- 负数
- 反码: 符号位是1,其余各位对原码取反。
- 补码: 反码加1.
- 移码: 补码的基础上,符号位取反(这里只讲一般的移码)
常用的字符串处理函数
C语言的标准库中含有很多非常有用的字符串处理函数。它们都要求以字符串作为参数,返回整数值或指向char的指针。
在头文件stdio.h
和string.h
中给出了字符串处理函数的原型,使用这些字符串处理函数时要引入相应的头文件。
字符串的输入和输出
在系统文件stdio.h
的定义中
- 函数
scanf()
和gets()
可用来输入字符串 - 函数
printf()
和puts()
可用来输出字符串
具体用法:
字符串输入函数gets(s)
参数s
是字符数组名。函数从输入流中得到一个字符串,遇到回车输入结束,自动将输入的数据和'\0'
送入数组中。采用该函数输入的字符串允许带空格。
实际上函数gets()
有返回值,如果输入成功则返回值是字符串第一个字符的地址,如果失败则返回NULL
。但一般情况下我们用它来输入字符串,因此不用关心它的返回值。字符串输出函数puts(s)
参数s可以是字符数组名或字符串常量。输出时遇到字符串结束符('\0'
)时自动将其转换成'\n'
,即输出后换行。同样的,如果成功输出则返回换行符’\n’,否则返回EOF
。
其中scanf()
和printf()
两个函数不再赘述。
字符串的复制、连接和比较
这里讲的三个功能函数,都在系统头文件string.h
中定义
字符串复制函数,函数原型-> char *strcpy(char *s1, char *s2)
该函数把字符串s2复制到s1(注意是S2->S1), 直到遇到’\0’为止。s1要有足够的空间容纳s2,且s1中的内容被覆盖,函数返回的是s1。
使用方式:
1 | strcpy(s1, s2); |
参数s1必须是字符型数组基地址,参数s2可以是字符数组名或字符串常量。
example:
1 | int i; |
字符串连接函数strcat(s1, s2)
参数s1必须是字符数组基地址,参数s2可以是字符数组名或字符串常量。strcat()
函数将s2接到s1的后面,此时,s1中原有的结束符’\0’被放置在连接后的结束位置上。 注意数组s1要足够大!!!,以便存放连接后的新字符串。
example:
1 | char str1[80] = "hello", str2[80], t[80]= "world"; |
C语言不像别的高级语言那样,能用+
拼接字符串!
字符串比较函数 strcmp(s1, s2)
和函数strcpy()
中对参数的要求不同, strcmp()
中的参数s1和s2可以是字符数组名或字符串常量。函数strcmp()
返回一个整数,给出字符串s1和s2的比较结果:
- 若s1和s2相等,返回0。
- 若s1 > s2, 返回正数。
- 若s1 < s2, 返回负数。
比较规则: 从字符串的首字符开始,依次比较(比较字符的ASCII码), 直到出现不同的字符或遇到’\0’为止。如果相同,则返回0,如果不同,则返回第一个不相同字符的比较结果-> 两个字符ASCII值的差,即第一个字符串中的字符减去第二个字符串中的字符。
小结
gets()
可以获取带有空格的字符串,而scanf()
则不行strcmp()
返回的是两个字符串第一个不同字符的ASCII值的差。strcat(s1, s2)
是把s2接到s1后面。
指针
关于指针,我实在想不到怎么解释,感觉就像它的名字一样。在计算机中像一根针一样指向一个内存地址。
我感觉下面这张图挺形象的↓👇
如果你用过git, 你一定知道什么叫分支或者版本回溯。我们看当前分支的时候,都会看到当前分支的分支名前面有个*
来表示我们现在所处的分支
其实这里的*
就是一个指针,指向我们当前的分支。
1 | dev |
指针和数组
其实数组和指针关系很近,数组变量名就是数组第一个元素的地址,因此数组名本身是一个地址即指针值。在访问内存方面,指针和数组几乎是相同的,当然也有区别,这些区别是微妙且重要的->指针是以地址作为值的变量,而数组名的值是一个特殊的固定地址,可以把它看作是指针常量。
首先给出如下定义:int a[100], *p;
因为数组是一段连续的空间,所以以下两条语句是等价的:
1 | // p指针等于数组的基地址+偏移量,这里便宜1个单位 |
再看几个循环打印数组内容的栗子(数组已经赋值):
1 | int a[10], *p; |
第二个方法:
1 | int a[10], *p; |
第三个方法:
1 | int a[10], *p = a; // 定义的时候赋值 |
这几个栗子充分的说明了指针和数组的关系。
数组、指针和函数
数组的形参实际上是一个指针。当参数传递时,主函数传递的是数组的基地址,数组元素本身是不被复制。
举个栗子:
1 | // 这里 int a[] 等价于 int *a |
指针和动态存储
为什么需要动态存储
一个程序通常需要各种变量来保存被处理的数据,但变量使用前必须要被定义且安排好存储空间(包括内存起始地址和存储单元大小)。C语言的全局变量、静态局部变量的存储是在编译时确定的。
- 其存储空间的分配时在程序开始执行前完成的
- 局部自动变量->在执行进入变量定义所在的语句时为他们分配存储单元(这种变量的大小也是静态确定的)
而静态方式存储的好处时实现方便、效率高。但某些问题不好解决,比如下面的这个栗子:
1 | int main(){ |
这个栗子中,每次求和的项数都可能不同,可能解决的办法就是定义一个很大的数组,以保证输入不会超出数组。
如果可以根据运行时的实际情况,让程序自己动态的分配存储空区,因此C语言为此提供了动态存储管理机制,允许程序动态申请和释放存储空间。
动态存储
关于动态存储的操作中,C语言提供了一组标准函数,定义在stdlib.h
里面.
先贴几个常用的函数:👇
动态存储分配函数->
void *malloc(unsigned size)
- 在内存的动态存储区中分配一连续的空间,长度为
·
size` - 申请成功->内存空间的起始地址,若不成功->NULL(0)
- 在内存的动态存储区中分配一连续的空间,长度为
计数动态存储分配函数->
void *calloc(unsigned n, unsigned size)
- 在内存的动态存储区中分配
n
个连续空间,每个的长度为size
- 分配后将存储块全部初始化为0
- 申请成功->返回一个指向分配内存空间起始地址的指针,若不成功->NULL(0)
- 在内存的动态存储区中分配
动态存储释放函数->
void free( void *ptr)
- 释放由动态存储分配函数申请得到的整块内存空间
- ptr指针要指向空间的首地址,如果是空指针,则啥都不做
分配调整函数->
void *realloc( void *ptr, unsigned size)
- 更改以前的存储分配。
ptr
必须是以前通过动态存储分配得到的指针- 参数
size
为现在需要的空间大小,如果分配失败则返回NULL,同时原ptr
指向存储块的内容不变。 - 如果分配成功,返回一片能存放大小为
size
的区块,并且保证该块的内容与原块一致。 - 如果
size
比原来小,则返回原块szie
范围内的数据(通俗理解: 截断了)
注意! malloc()对所分配的存储块不做任何事情,calloc()对整个区域进行初始化。
结构体
其定义语法:
1 | struct 结构名{ |
结构体的定义和初始化(使用), Demo:
1 | struct 结构名 变量名; |
结构体的使用(进阶)
混合定义-> 其实就是在定义结构体的时候顺便声明变量
example:1
2
3
4
5
6
7
8
9
10struct 结构名{
类型名 结构成员名1;
类型名 结构成员名2;
类型名 结构成员名3;
}结构变量名表;
// demo
struct student
int num;
char name[10];
}s1, s2;无类型名定义
无类型名指定义结构体变量时省略结构名。
example:1
2
3struct {
类型名 结构成员;
}结构变量名表;这种定义只有结构变量名表里的才能用.
结构体套娃
结构体的嵌套定义, Demo:
1 | struct address{ |
再随便说说
PTA的刷题记录我都放在Github上了, 学的不精欢迎各位大佬提issue或pr指出!~😊