C语言文件IO基础,静态库与动态库的创建

输入:从文件到内存。
输出:从内存到文件。

标准IO

C语言标准的一系列用来进行输入和输出的函数。

  1. 可跨平台。
  2. 标准IO通过缓冲机制,减少系统调用。实现最大化提升效率。
  3. 文件流指针。通过一个结构体来操作。

文件的基本概念

  1. 一组相关数据的有序集合。(视频、音频、图片)
  2. 文件名:这组数据集合的名字。
  3. 文件的类型:
      • 普通文件。
      1. ASCII码文件。(文本文件)
      2. 二进制文件。
    1. d 目录文件。
    2. c 字符设备文件。
    3. b 块设备文件。
    4. l 链接文件。
    5. p 管道文件。
    6. s 套接字文件。(socket)
  4. 系统调用:操作系统提供给应用程序的接口。
  5. 缓冲区:减少系统调用的次数,减少系统的开销。
  6. FILE指针:结构体类型的别名。(标准IO中已有的结构体)
  7. 流:打开文件时就会有缓冲区。(存在与内存和缓存之间)对文件的操作就是对流的操作。标准IO就是通过流来操作文件。

  1. 文本流:ASCII码文本文件。
  2. 二进制流:二进制文件。

缓冲区

  1. 全缓存:当缓冲区满了(一般为1024字节)或者程序运行结束,再或者强制刷新缓冲区(fflush)时才会刷新缓存。
  2. 行缓存:当缓冲区满了或者程序运行结束,再或者强制刷新缓冲区,再或者遇到换行符时会刷新缓存。
  3. 不缓存:没有缓冲区,直接输出。当程序运行起来时,有三个文件默认打开(标准输入stdin、标准输出stdout、标准出错stderr)stderr是没有缓冲区的。

相关函数

  1. fopen:FILE *fopen(const char *open, const char *mode)
    1. 功能:打开一个流。

    2. 参数1:要打开的文件的路径。

    3. 参数2:打开方式。(r:以只读方式打开,该文件必须存在。
      r+:可读可写方式打开,该文件必须存在。
      w:以只写的方式打开文件,若文件存在清空,不存在则新建。
      w+:可读可写方式打开,文件不存在新建,文件存在清空文件。
      a:只写,文件不存在新建,存在追加在末尾。
      a+:可读可写,文件存在追加在末尾,不存在新建。)

    4. 返回值:成功返回流指针,失败返回NULL。

  2. fclose:FILE *fclose(FILE *fp);
    1. 功能:关闭一个流。
    2. 参数:要关闭的流指针。
    3. 返回值:成功返回1,失败返回-1(EOF)
    4. 注意:EOF只能判断是否到文本文件的末尾,不能判断二进制文件。

读写操作

  1. fgetc:int fgetc(FILE *Stream)
    1. 功能:从指定的流中读取数据。
    2. 参数:需要操作的流指针。
    3. 返回值:成功返回读到的字符ASCII码值,失败返回-1。
  2. fputc:int fputc(int c, FILE *Stream)
    1. 功能:从指定流中写入数据。
    2. 参数1:要输出的字符。
    3. 参数2:需要输出的流指针。
    4. 返回值:成功返回输出的字符ASCII码值,失败返回-1。
  3. feof:int feof(FILE * Stream);(这里注意避坑。feof的非0值返回在末尾标记的下一个)
    1. 功能:判断是否到文件的末尾。判断文本文件的同时也可用于二进制文件。
    2. 参数:要操作的流。
    3. 返回值:为0表示没有到达文件末尾,非0表示到达文件末尾。
  4. fgets:char* fgets(char* s, int size, FILE* Stream);
    1. 功能:按行读文件。
    2. 参数1:读到空间的首地址。
    3. 参数2:读的大小。
    4. 参数3:读哪个流。
    5. 返回值:成功返回读到的字符串的首地址,失败或读完返回NULL。
  5. fputs:int fputs(const char* s, FILE* Stream);
    1. 功能:向文件输出一行字符串。
    2. 参数1:需要输出的字符串首地址。
    3. 参数2:要输出的流。
    4. 返回值:成功返回非负数,失败返回-1。
  6. fread:size_t fread(void* ptr, size_t size, size_t number, FILE* stream);
    1. 功能:按块读。
    2. 参数1:读到内存空间的首地址。
    3. 参数2:一个块的大小。(一般传1不会漏数据)
    4. 参数3:块的数量。(一般传sizeof(ptr))
    5. 参数4:要操作的流。
    6. 返回值:成功返回读到的块数(向下取整,其余的不要),失败返回-1。
  7. fwrite:size_t fwrite(const void* ptr, size_t size, size_t number, FILE* stream);
    1. 功能:往流中写。
    2. 参数1:需要写入内容的内存空间的首地址。
    3. 参数2:一个块的大小。(一般传1不会漏数据)
    4. 参数3:块的数量。(一般传sizeof(ptr))
    5. 参数4:要写入哪个流。
    6. 返回值:成功返回读到的块数(向下取整,其余的不要),失败返回-1。
  8. fseek:int fseek(FILE *stream,long offset,int origin);
    1. 功能:自定义文件位置。
    2. 参数1:正在操作的流。
    3. 参数2:偏移量。(正数以参照物为基准向后,负数向前)
    4. 参数3:参照物。
    5. 返回值:
      origin有三种位置,一种是起始位置:SEEK_SET,第二种是当前位置:SEEK_CUR,第三种是末尾位置:SEEK_END。
      fseek(fr, 0, SEEK_SET) //开头光标为参照点,光标走到文件开头
      fseek(fr, -len, SEEK_CUR) //当前光标开始向前偏移
      fseer(fr, -len, SEEK_END) //从末尾开始向前偏移len
  9. ftell:int ftell(FILE *stream);
    1. 功能:返回文件流中当前光标位置。

以上标准 IO打开文件自带缓冲区所以默认会打开流,下面的文件 IO操作系统不带缓冲区将不会自动开启流

文件IO

  1. 不带缓冲区,每次读写都由系统调用。
  2. 文件描述符。(非负的整数)
  3. 一般用于设备文件的操作。

相关函数

  1. open: 头文件:<sys/types.h><sys/stat.h><fcntl.h>
    1. int open(const char* pathname, int flags);(文件必须存在)在这里插入图片描述

    2. int open(const char* pathname, int flags, mode_t mode);
      这里mode 权限给了 0777 但是实际却不是0777权限,这里有个文件掩码 可以通过 umask 查看文件掩码 最终的权限0777与0002在先取反再与操作得到真正的文件权限 0775在这里插入图片描述
      在这里插入图片描述

    3. 打开方式(flags),以下参数必选其一
      1.O_RDONLY:只读打开。
      2.O_WRONLY:只写打开。
      3.O_RDWR:可读可写打开。
      其余的与上面参数进行或运算:
      4.O_CREAT:文件不存在则新建,但必须使用前3个参数其中一个和creat进行或操作。
      5.O_EXCL:文件存在返回错误信息。
      6.O_NOCTTY:若文件为终端,那么终端不可以作为这个进程的控制终端。(阻塞)
      7.O_TRUNC:文件存在清空。
      8.O_APPEND:文件存在追加到末尾。

    4. mode:权限,一般用8进制表示。

    5. 返回值:打开成功文件的描述符。

  2. close:头文件:<unistd.h>
    1. int close(int fd); 入参为open打开的文件描述符。
    2. 成功返回0,失败-1。

读写操作
头文件:<unistd.h>

  1. read:ssize_t read(int fd, void* buf, size_t count);
    1. 参数1:文件描述符。
    2. 参数2:指定存储器读出数据的缓冲区。
    3. 参数3:指定读出的字节数。
    4. 返回值:成功返回读到的字节数,0表示已到达末尾,-1出错。
  2. write:ssize_t write(int fd, void *buf, size_t count);
    1. 参数1:文件描述符。open的返回值。
    2. 参数2:指定存储器写入数据的缓冲区。
    3. 参数3:指定写入的字节数。
    4. 返回值:成功返回写的字节数,0表示到达文件末尾,-1出错。

相关函数

  1. lseek:off_t lseek(int fd, off_t offset, int whence);
    1. 功能:创建空洞文件,实现光标的移动。
    2. 参数1:文件描述符。
    3. 参数2:偏移量。
    4. 参数3:起始位置。同fseek一样的宏
    5. 返回值:成功当前读写位置,失败-1。
    6. 空洞文件:在未下载完成时就已经占据了文件的全部大小的文件。
    7. 空洞文件的创建:
      1. 只写方式打开文件。
      2. 使用lseek改变文件下标位置。
      3. 向该标记末尾添加\0
      4. 关闭文件。
        在这里插入图片描述

目录相关操作

打开目录
头文件:<sys/types.h> <dirent.h>

  1. opendir:DIR* opendir(const char* name);
    1. 参数:路径。
    2. 返回值:成功返回DIR*类型的流。失败返回NULL。

关闭目录
头文件:<sys/types.h> <dirent.h>

  1. closedir:int closedir(DIR* dir);
    1. 参数:目录流,opendir的返回值。

目录操作
头文件:<dirent.h>

  1. struct dirent* readdir(DIR* dir);
    1. 打开目录文件。
    2. 返回值:成功返回dirent结构体,失败返回NULL。
    3. struct dirent {
      ino_t d_ino; /* Inode数*/
      off_t d_off; /* 不透明值 */
      unsigned short d_reclen; /* 记录的长度 */
      unsigned char d_type; /* 文件类型 */
      char d_name[256]; /* 文件名 */
      };

静态库与动态库的区别

库:是一个二进制文件,包含的代码可以被程序调用。

静态库的特点

  1. 编译时把静态库中相关二进制文件拷贝到可执行文件中,运行时不需要链接库。
  2. 程序运行时无需加载库,运行速度快。(优点)
  3. 占用更多的内存空间。静态库升级后程序需要重新编译。(缺点)

动态库的特点

  1. 编译时仅记录使用哪个共享库(动态库)中的哪个符号(函数),不复制库中相关代码,运行时加载共享库。
  2. 程序不包含库中的代码,代码尺寸比较小。(优点)
  3. 库加载方便,升级后无需重新编译。
  4. 使用较多。

静态库的创建

  1. 编写源代码。(去掉main函数)
  2. 将源代码生成对应的.o文件。
  3. ar crs 命令 生成静态库。
  4. 静态库的命名规范:lib+库名.a
    在这里插入图片描述
  5. ar:库文件维护程序的名称。
    1. c:创建一个库,不管是否存在,都创建。
    2. r:在库中插入模块。
    3. s:创建目标文件索引,这个参数在创建较大的库时能加快时间。
  6. 编译时使用 -L+静态库的目录 -l+库名 完成链接。
    在这里插入图片描述

动态库的创建

  1. 编写源代码。(去掉main函数)

  2. 将源代码生成对应的.o文件。(这里需要多使用 -fPIC 参数生成与位置无关的代码,可以在任何位置执行)在这里插入图片描述

  3. 创建共享库。(gcc -shared)

  4. 共享库的命名规范:lib+库名.so.版本号在这里插入图片描述

  5. 共享库创建软连接。(为了让编译器编译的时候方便找到自己写的共享库)在这里插入图片描述

  6. 链接共享库。(注意:此时是在系统库目录下找自己写的共享库是找不到的)在这里插入图片描述

  7. 将自己写的共享库软连接复制到系统库路径下。/usr/lib 或者 lib
    在这里插入图片描述
    添加到系统库路径下后,编译链接时就不用 -L参数
    在这里插入图片描述

  8. 运行

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值