目录
一、IO
1.标准IO和文件IO
区别:
标准IO:
1.标准IO是由库函数系统提供的,由ANSI C标准定义
2.是带缓冲区的操作,运行效率较高
3.支持跨平台的
4.标准IO操作的依据一般是流指针
文件IO:
1.文件IO是由操作系统提供的,由POSIX(可移植操作系统接口)定义
2.没有缓冲区,运行效率没有标准IO高
3.不支持跨平台的
4.文件IO操作的依据一般是文件描述符(非负整数)
2.系统调用和库函数
1.系统调用一般提供基础的功能,库函数提供较为复杂的功能
2.系统调用一般不能重写,库函数可以重写
3.系统调用运行的时间属于机器时间,库函数运行时间属于用户时间
4.系统调用运行空间属于内核空间,库函数运行空间属于用户空间
5.系统调用的返回值一般是非负整数,库函数的返回值不一定
6.系统调用的运行效率没有库函数的高
7.系统调用的移植性没库函数的好
3.文件描述符和流指针
文件描述符是一个用于标识已打开文件的整数。在Unix和类Unix系统中,文件描述符是非负整数,通常是一个小的非负整数。每个打开的文件都会被分配一个唯一的文件描述符,用于在后续的文件操作中标识该文件。
流指针是一个用于指示文件位置的指针。它指向文件中的某个位置,可以用来读取或写入数据。流指针通常是一个结构体,包含了文件描述符、当前位置等信息。
在C语言中,文件描述符和流指针是通过文件操作函数来使用的。文件操作函数提供了一系列操作文件的函数,如打开文件、读取文件、写入文件等。
4.标准IO
全缓冲:
缓冲区满了才刷新缓冲区,或者强制刷新缓冲区
行缓冲:
碰到换行符刷新缓冲区、缓冲区满了刷新、或者强制刷新缓冲区
不缓冲:
所有的信息到缓冲区之后直接到文件
4.1对文件进行操作
4.1.1打开文件
fopen:
函数功能:打开由参数1描述的文件,打开的方式由参数2确定
函数参数1:需要被打开的文件的路径
函数参数2:打开的方式
r : 以只读的形式打开文件,文件存在则打开,不存在则报错
r+ : 以读、写的形式打开文件,文件存在则打开,不存在则报错
w : 以只写的形式打开文件,文件存在则清空打开,文件不存在则新建
w+ : 以读、写的形式打开文件,文件存在则清空打开,文件不存在则新建
a : 以追加的形式打开文件,文件存在则追加,文件不存在则新建
a+ : 以可读可写(追加)的形式打开文件,文件存在则追加,文件不存在则新建
函数返回值:成功返回:文件流指针
失败返回NULL,并且更新errno
4.1.2操作文件
fgetc
案例:模拟cat命令
函数功能:从stream指示的文件中读取字符
函数参数:需要读字符的文件流
函数返回值:成功返回读到的字符可以是(EOF,表示读完了),失败返回-1
fputc
函数功能:给stream写了一个字符c
函数参数1:被写的字符
函数参数2:给谁写
函数返回值:成功返回写入的字符,失败返回-1
以行读写:
fgets
案例:判断一个文件有多少行
函数功能:按行读
函数参数1:读到哪
函数参数2:读多少(实际读到的是size-1,最后一个空间给\0预留) 所以fgets相较于gets是安全的
函数参数3:从哪读
函数返回值:成功返回非0,返回NULL的时候是出错或者没有可读的字符了
fputs
函数功能:给文件写
函数参数1:从哪写
函数参数2:给谁写
函数返回值:成功返回非负整数,失败返回-1
以对象读写:
fread:
fwrite:
案例:数据的读和写
myhead.h
myread.c
函数功能:读
函数参数1:读到哪
函数参数2:一次读多少
函数参数3:总共读多少次数
函数参数4:从哪读
函数返回值:成功返回成功读或写的成员的个数,失败返回0
函数功能:写
函数参数1:从哪写
函数参数2:一次写读多少
函数参数3:总共写多少次
函数参数4:往哪写
函数返回值:成功返回成功读或写的成员的个数,失败返回0
fprintf
案例:每隔3秒获取时间写入文件
函数功能:往指定的流里面输出东西
函数参数1:指定的流
函数参数2……:同printf
fscanf
函数功能:从指定的流里面获取信息
函数参数1:指定的流
函数参数2……:同scanf
关闭文件:
fclose:
main函数传参:
标准IO相关函数:
函数功能:关闭由参数stream指示的流
函数参数:需要被关闭的流
函数返回值:成功返回0,失败返回-1并且设置errno
fflsh
函数功能:强制刷新由参数 1 指定的流
函数参数如果为 NULL ,则会刷新所有已打开的输出流
函数返回值:成功返回 0 ,失败返回 -1 并且更新 errno
feof
判断文件是否到末尾
fseek
函数功能:给指定的流从参数 3 描述的位置移动参数 2 个大小的位置
参数 1 :给谁移动光标
参数 2 :移动多少
参数 3 :从那开始:
SEEK_SET 从文件开头移动
SEEK_CUR 从文件当前光标指向的位置开始移动
SEEK_END 从文件末尾开始移动
返回值:
成功返回 0 ,失败返回 -1 并且更新 errno
ftell
函数功能:计算由参数 stream 指示的流当前光标处在的位置
函数参数:指定的文件流
函数返回值:成功返回当前处在的位置,失败返回 -1 ,并更新 errno
fileno
函数功能:获取文件流指针里面的文件描述符
5.文件IO
5.1相关函数
open
函数功能:打开文件
函数参数 1 :需要打开的文件路径
函数参数 2 :打开的方式
必须包含如下的其中一个
O_RDONLY : 只读
O_WRONLY : 只写
O_RDWR : 可读可写
还可以有以下的:
O_APPEND: 以追加的形式打开
O_CREAT : 文件存在则打开,不存在则按照 mode 参数描述的权限创建一个普通文件
函数参数 3 :如果文件存在则打开,不存在则按照此参数描述的创建一个普通文件
函数返回值:成功返回文件描述符(非负的最小整数) file description fd
失败返回 -1 ,并且更新 errno
read
函数功能:从 fd 描述的文件中读取 count 个字节的数据给 buf 开始的空间
函数参数 1 :从哪读
函数参数 2 :读到哪
函数参数 3 :读多少
函数返回值:成功返回实际上读到的数目,失败返回 -1 ,并且更新 errno
write
函数功能:把数据从 buf 指示的空间里面写道 fd 里,写了 count 个
函数参数 1 :往哪写
函数参数 2 :从哪写
函数参数 3 :写多少
函数返回值:成功返回写入的个数,失败返回 -1 并且更新 errno
close
函数功能:关闭文件
函数参数:需要关闭的文件的文件描述符
函数返回值:成功返回0,失败返回-1并且更新errno
6.静态库和动态库
6.1静态库制作
步骤1:将c源文件生成对应的.o文件
1: ~/test/3static_lib$ gcc -c add.c -o add.o
2: ~/test/3static_lib$ gcc -c sub.c -o sub.o
3: ~/test/3static_lib$ gcc -c mul.c -o mul.o
4: ~/test/3static_lib$ gcc -c div.c -o div.o
步骤2:使用打包工具ar将准备好的.o文件打包为.a文件 libtest.a
1: ~/test/3static_lib$ ar -rcs libtest.a add.o sub.o mul.o div.o
2)静态库使用
静态库制作完成之后,需要将.a文件和头文件一起发布给用户。
假设测试文件为main.c,静态库文件为libtest.a头文件为head.h
编译命令:
1:~/test/4static_test$ gcc test.c -L./ -I./ -ltest -o test
参数说明:
-L:表示要连接的库所在目录
-I./: I(大写i) 表示指定头文件的目录为当前目录
-l(小写L):指定链接时需要的库,去掉前缀和后缀
2.3动态库的制作及使用
共享库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。
动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
按照习惯,一般以“.so”做为文件后缀名。共享库的命名一般分为三个部分:
前缀:lib
库名称:自己定义即可
后缀:.so
所以最终的动态库的名字应该为:libxxx.so
6.2动态库制作
1)找到函数定义的地方,将.c文件编译成.o文件(.o文件里面的是机器语言):
gcc -c add.c -o add.o
2)把.o文件编译生成libadd.so(注意lib是前缀,add才是库名,.so是后缀):
gcc -fPIC -shared add.o -o libadd.so
也可以一步到位:gcc -fPIC -shared add.c -o libadd.so
注意我这里头文件没有跟.c文件放同一个目录,所以要指定一下头文件的位置gcc -fPIC -shared add.o -o libadd.so -I../include (等一下我们讲指定的问题)
3)把生成的库放到/lib下:
4)编译命令
gcc main.c -o main -Iinclude/ -Llib/ -ladd
要永久设置则修改文件 gedit ~/.bashrc
二、数据结构
1.线性结构
- 存储方式简单,可以通过下标或指针直接访问元素。
- 插入和删除操作相对容易,只需要调整指针或移动元素的位置。
- 查询操作的效率较高,可以通过下标或指针快速定位元素。
1.1顺序结构
数据元素按照顺序存储在连续的内存空间中,可以通过下标直接访问元素。
1.2链式结构
链式结构:数据元素通过指针链接在一起,每个元素包含数据和指向下一个元素的指针。
1.3栈
一种特殊的线性结构,遵循"先进后出"(LIFO)的原则,只能在栈顶进行插入和删除操作。
1.4队列
一种特殊的线性结构,遵循"先进先出"(FIFO)的原则,可以在队尾插入元素,在队头删除元素。
2.树形结构
- 树形结构能够更好地表示具有层次关系的数据,如文件系统、组织结构等。
- 树形结构的遍历方式有多种,如前序遍历、中序遍历、后序遍历等。
- 二叉树的特殊性使得它的插入、删除和查询操作的时间复杂度较低。
2.1二叉树
二叉树:每个节点最多有两个子节点的树结构。
2.2平衡二叉树
一种特殊的二叉树,左右子树的高度差不超过1,以提高搜索效率。
2.3赫夫曼树
一种特殊的二叉树,用于数据压缩,频率高的字符在树的顶部,频率低的字符在树的底部。
2.4线索二叉树
在二叉树的节点中添加指向前驱和后继节点的线索,以提高遍历效率。
2.5树的的顺序存储
用数组的方式存储树的节点,通过计算节点的索引和父节点的索引之间的关系来访问节点。
2.6树的的链式存储
通过指针链接节点来存储树的结构。
3.网状结构
- 网状结构能够更好地表示元素之间的多对多关系,如社交网络、交通网络等。
- 网状结构的存储方式可以选择顺序存储或链式存储,根据具体情况选择合适的方式。
- 网状结构的遍历和搜索操作相对复杂,通常需要使用图算法来解决。
3.3网状结构的顺序存储
通过矩阵或数组来表示元素之间的关系。
3.4网状结构的链式存储
通过指针链接元素来表示元素之间的关系。
4.算法
- 可读性:算法的代码是否易于理解和维护。
- 可靠性:算法在各种输入情况下是否能够正确地得到预期的结果。
- 可扩展性:算法是否能够处理大规模的数据集。
- 时间复杂度和空间复杂度:算法的执行时间和所需的内存空间是否在可接受的范围内。
4.1常见的算法
常见的算法包括排序算法、搜索算法、图算法等,用于解决各种问题。
4.2算法的好坏
算法的好坏可以通过时间复杂度和空间复杂度来评估,好的算法应该具有较低的时间复杂度和空间复杂度。