- 1.库函数IO接口
- 1.1 fopen
- 1.2 fwrite
- 1.3 fread
- 1.4 fseek
- 1.5 fclose
- 2. 系统调用IO接口
- 2.1 open
- 2.2 read
- 2.3 write
- 2.4 lseek
- 2.5 close
- 2.6 例子
- 3. 文件描述符
- 4. 重定向
- 5. dup2 系统调用
- 6. 文件描述符与文件流指针
- 7. 动态库和静态库
- 7.1 静态库与动态库
- 7.2 动态库和静态库的生成
- 7.3 动态库和静态库的使用
- FILE *fopen(char *pathname,char *mode)
- pathname:文件路径名;mode:文件的打开方式
- r:只读方式打开文件,文件必须存在
- r+:读写方式打开文件,文件必须存在
- w:只写方式打开文件,文件不存在则自动创建,存在则清空内容
- w+:读写方式打开文件,文件不存在则自动创建,存在则清空内容
- a:追加写方式打开文件,文件不存在则自动创建,存在则写入数据总是写入文件末尾
- a+:读+追加写方式打开文件,文件不存在则自动创建。存在则写入数据总是写入文件末尾
- b:二进制方式打开文件(在系统调用中IO接口并不取反文本和二进制数据,统一按照二进制处理。但是库函数不一样,区分文本和二进制,默认文本操作,但是有点文本一个字符占据多个字节。例如:一个文件中有10个字节数据,但是读取的时候读取完毕后读取大小是9,这个9表示的不是9个字节而是9个字符。
) - 返回值:打开文件成功则返回一个FILE*文件流指针作为文件的操作句柄,失败返回NULL 。
- size_t fwrite(const void* data, size_t bsize, size_t nmemb, size_t nmemb, FILE*fp)
- data:要写入文件的数据的所在空间首地址
- bsize:块大小;
- bsize* nmemb:实际要写入文件的大小
- fp:fopen返回的文件流指针,表示要操作哪一个文件
- 返回值:成功返回实际完整操作的块个数;失败返回0
- 注意:r+/w/w+ 方式打开文件之后, 文件的默认读写位置在文件起始,如果文件本身有数据,就会覆盖,并且读写位置会随着数据的写入向后偏移;a/a+方式打开文件之后,读默认是从文件起始读的,但是如果写会使读写位置跳转到文件末尾,将数据追加到末尾。
- size_t fread(void *buf, size_t bsizew, size_t nmemb, FILE *fp)
- buf:一块空间的首地址,用于存放从文件中读取的数据
- bsize:块大小;
- nmemb:块个数;
- bsize*nmemb:就是实际要读取的数据的大小
- fp:文件流指针
- 返回值:成功返回实际操作的完整块个数,读取到文件末尾了返回0,出错了返回0。
- 注意:这个函数返回值存在多义性,返回0的时候无法直接确定是出错了还是读取到文件末尾了。
例如:一个文件100字节,我们现在读取数据块大小200,块个数1,因为没有读取到完整的一块,所以我们不能确定是出错了还是正常的。
因此,建议fread读取数据的时候块大小设置为1,想要读取的长度设置为块个数,这样返回值就能告诉我们读取了多少数据。
改变文件读写位置,跳转到哪里就从哪里开始读写。
- int fseek(FILE *fp, int offset,int whence)
- fp:文件流指针
- offset:偏移量
- whence:偏移起始位置
- SEEK_SET-起始/SEEK_CUR-当前/SEEK-END-末尾
- 返回值:成功返回0;返回失败-1
- int open(char *pathname,int flag,mode_t mode)
- pathname:要打开的文件路径名
- flag:文件打开标志——决定了文件的打开方式
必选标志:O_RDONLY-只读/ O_WRONLY-只写 /O_RDWR-读写
可选标志:O_APPEND-追加写 | O_CREAT-文件不存在则创建 | O_TRUNC-文件不存在则截断为0 | O_EXCL-文件存在则报错
w+ :读写+创建+截断 O_RDWR | O_CREAT |O_TRUNC
a+ :读+追加写,创建 O_RDWR | O_APPEND |O_CREAT - mode:文件的权限,通常采用八进制数字设定 0777,如果使用了O_CREAT就一定要有第三个参数。
文件的创建权限是受到umask掩码影响的。实际文件得到的权限: mode&(umask) - 返回值:成功返回一个非负整数-文件描述符-是文件的操作句柄;失败返回-1
- mode_t umask(mode_t mask); 通常在程序 起始阶段调用 umask(0)将当前进程的创建码设置为0。
- size_t read(int fd, void *buf, int len)
- fd:文件描述符-操作句柄;
- data:要写入文件数据的空间首地址;
- len:想要写入文件的数据长度
- 返回值:成功返回实际写入文件的长度,失败返回-1;
- ssize_t read(int fd, void *buf,int len);
- fd:open返回的文件描述符-操作句柄-表示操作哪个文件
- buf:一块空间地址,用于存放读取到的数据
- len:要读取的数据长度
- 返回值:成功返回实际读取到的数据长度;失败返回-1
- ssize_t write(int fd, void *data, int len )
- fd:文件描述符-操作句柄
- data:要写入文件的数据的空间首地址
- len:想要写入文件的数据长度
- 返回值:成功返回实际写入文件的数据长度;失败返回-1
- off_t lseek(int fd, off_t offset, int whence)
- fd:文件描述符
- offset:偏移量
- whence:偏移量起始位置 SEEK_SET/SEEK_CUR/SEEK_END
- 返回值:成功返回跳转后的位置相对于文件起始位置的长度(额外用法:跳到文件末尾则返回文件长度);返回失败返回去-1
- int close(int fd):关闭文件释放资源
-
问题描述
使用代码打开当前路径下的“bite”文件(如果文件不存在在创建文件),向文件当中写入“i like linux!”. 在从文件当中读出文件当中的内容, 打印到标准输出当中; 关闭文件描述符。 -
代码截图
-
结果截图:
open返回的一个非负整数
文件描述符本质是什么?如何表示操作的文件?
文件描述符的本质:内核中,进程打开文件IO信息指针数组的一个下标。
- 改变数据的流向,让原本写入a文件的数据,不再写入a文件,而是写入b文件。
- 一个进程运行起来默认会打开三个文件:标准输入,标准输出,标准错误,这三个文件占据的描述符就是 0-标准输入, 1-标准输出,2-标准错误。
- 重定向原理:改变文件描述符这个下标所对应的文件描述信息指针(操作的描述符 没有改变,但是改变了操作的文件)
- ls>>text.txt 实现原理:
ls 是一个指令程序,运行时会启动进程,进程由 shell 创建,shell 创建了 ls 子进程之后只需要将这个进程的 1 号描述符对应的文件信息指针改变就可以实现在不改变 ls 程序的情况下改变数据的流向。
-
函数原型如下:
#include
int dup2(int oldfd, int newfd); -
让 newfd 赋值 oldfd 对应的描述信息,成功则关闭newfd原来指向
示例代码
#include6. 文件描述符与文件流指针int main() { close(1); int fd = open("myfile", O_WRONLY|O_CREAT, 00644); if(fd < 0){ perror("open"); return 1; } printf("fd: %dn", fd); fflush(stdout); close(fd); exit(0); }
- 文件流指针:struct FILE*
- 文件描述符: int
- 注意:类型不同不能混用
- 文件流指针中封装了文件描述符,除此之外还包含了一个用户缓冲区
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
-
库:很多已经写好的功能代码,所打包而成的一个文件(库中不能有main函数)
-
库的命名 lib 作为前缀,中间名称,最后.so作为动态库后缀,.a作为静态库后缀
-
vi 三个文件
-
现将所有的 .c 编译汇编完成后会生成自己的 .o 二进制指令文件
gcc -fPIC -c $^ -o $@
-
将所有的 .o 二进制指令文件打包到一起(要生成的不是可执行程序而是库)
gcc --shared $^ -o lib**.so -
生成一个动态库:gcc --shared $^ -o lib**.so
-
生成一个静态库:ar -cr lib**.a **.o **.o
- 生成可执行程序的时候使用:
- (1)使用 -l 来指定要链接是库的名称(前提是库文件需要放到指定路径下:/usr/lib64/)
- (2)如果设置环境变量,将库文件所在目录加入到环境变量路径中:export
LIBRARY_PATH=${LIBRARY_PATH}:./ (注意这里没有空格) - (3)使用gcc -L 选项指定库文件的所在路径(常用于链接静态库——因为静态库没有依赖,运行时不需要加载)
gcc main.c -o main -L./-lchild(注意./-lchild中间没有空格)
- 运行可执行程序的时候使用:(仅限于动态库——因为只有链接动态库,运行程序的时候才需要加载)
静态库使用时是把库中用到的代码直接放入到可执行程序,动态库使用时只记录函数符号信息,因此运行时依赖
- (1)运行时设置环境变量中的库文件加载路径(与上面的 export
LIBRARY_PATH=${LIBRARY_PATH}:./ 是成对使用的)
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:./
- 一个目录下既有动态库也有静态库,同名,gcc生成可执行程序时默认优先链接动态库(gcc默认的链接方式就是动态链接(链接动态库))



