目录

一、mystdio.h

代码思路分析

二、mystdio.c

1. 辅助函数 BuyFile

2. 文件打开函数 MyFopen

3. 文件关闭函数 MyFclose

4. 数据写入函数 MyFwrite

1、memcpy(file->outbuffer + file->bufferlen, str, len);

2、按位与(&)运算的作用

运算规则

5. 缓冲区刷新函数 MyFFlush

1、缓冲区空检查

2、数据写入系统调用

3、数据同步到磁盘

4、重置缓冲区

三、Makefile

四、Makefile-st


一、mystdio.h

#pragma once
// 这是一个预处理指令,确保头文件在编译时只被包含一次,防止重复包含#include <stdio.h>
// 包含标准输入输出库,提供基本的文件操作函数原型#define MAX 1024
// 定义输出缓冲区的大小为1024字节// 定义刷新模式的标志位
#define NONE_FLUSH (1<<0)   // 不自动刷新
#define LINE_FLUSH (1<<1)   // 行缓冲模式(遇到换行符时刷新)
#define FULL_FLUSH (1<<2)   // 全缓冲模式(缓冲区满时刷新)// 自定义文件结构体,模拟标准库的FILE结构
typedef struct IO_FILE
{int fileno;             // 文件描述符(底层操作系统文件标识)int flag;               // 文件打开模式标志char outbuffer[MAX];    // 输出缓冲区int bufferlen;          // 当前缓冲区中的数据长度int flush_method;       // 缓冲刷新策略(NONE/LINE/FULL)
}MyFile;// 函数声明/*** @brief 打开文件并初始化MyFile结构体* @param path 文件路径* @param mode 打开模式("r"/"w"/"a"等)* @return 成功返回MyFile指针,失败返回NULL*/
MyFile *MyFopen(const char *path, const char *mode);/*** @brief 关闭文件并释放资源* @param fp MyFile指针*/
void MyFclose(MyFile *fp);/*** @brief 向文件写入数据* @param fp MyFile指针* @param str 要写入的数据指针* @param len 要写入的数据长度* @return 成功写入的字节数*/
int MyFwrite(MyFile *fp, void *str, int len);/*** @brief 手动刷新缓冲区* @param fp MyFile指针*/
void MyFFlush(MyFile *fp);

代码思路分析

这段代码实现了一个简单的文件I/O封装库,模拟了标准C库中的文件操作函数。主要特点包括:

  1. 缓冲机制

    • 使用outbuffer作为输出缓冲区,减少直接系统调用的次数

    • 支持三种缓冲策略:无缓冲、行缓冲和全缓冲

  2. 结构设计

    • MyFile结构体封装了文件描述符、缓冲区及状态信息

    • 类似于标准库的FILE结构,但更简化

  3. 功能模拟

    • MyFopen:模拟fopen,打开文件并初始化结构体

    • MyFwrite:模拟fwrite,写入数据到缓冲区

    • MyFFlush:模拟fflush,强制刷新缓冲区

    • MyFclose:模拟fclose,关闭文件并释放资源

  4. 缓冲策略

    • NONE_FLUSH:每次写入都立即刷新(无缓冲)

    • LINE_FLUSH:遇到换行符时刷新(行缓冲)

    • FULL_FLUSH:缓冲区满时刷新(全缓冲)


二、mystdio.c

#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>static MyFile *BuyFile(int fd, int flag)
{MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;f->bufferlen = 0;f->fileno = fd;f->flag = flag;f->flush_method = LINE_FLUSH;memset(f->outbuffer, 0, sizeof(f->outbuffer));return f;
}MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;if(strcmp(mode, "w") == 0){flag = O_CREAT | O_WRONLY | O_TRUNC;fd = open(path, flag, 0666);}else if(strcmp(mode, "a") == 0){flag = O_CREAT | O_WRONLY | O_APPEND;fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0){flag = O_RDWR;fd = open(path, flag);}else{//TODO}if(fd < 0) return NULL;return BuyFile(fd, flag);
}
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;MyFFlush(file);close(file->fileno);free(file);
}
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 拷贝memcpy(file->outbuffer+file->bufferlen, str, len);file->bufferlen += len;// 2. 尝试判断是否满足刷新条件!if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n'){MyFFlush(file);}return 0;
}
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;// 把数据从用户拷贝到内核文件缓冲区中int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;fsync(file->fileno);file->bufferlen = 0;}

1. 辅助函数 BuyFile

BuyFile 函数在这个自定义 I/O 库中模拟的是标准 C 库(如 glibc)中 FILE 结构体的初始化过程。

/*** @brief 分配并初始化MyFile结构体* @param fd 文件描述符* @param flag 文件打开标志* @return 成功返回MyFile指针,失败返回NULL*/
static MyFile *BuyFile(int fd, int flag)
{// 分配MyFile结构体内存MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;// 初始化结构体成员f->bufferlen = 0;           // 缓冲区初始为空f->fileno = fd;             // 设置文件描述符f->flag = flag;             // 设置文件打开标志f->flush_method = LINE_FLUSH; // 默认使用行缓冲模式memset(f->outbuffer, 0, sizeof(f->outbuffer)); // 清空缓冲区return f;
}

功能分析

  • 这是一个静态辅助函数,用于创建和初始化MyFile结构体

  • 设置默认缓冲策略为LINE_FLUSH(行缓冲)

  • 清空输出缓冲区,确保初始状态干净

2. 文件打开函数 MyFopen

/*** @brief 打开文件并初始化MyFile对象* @param path 文件路径* @param mode 打开模式("w"/"a"/"r")* @return 成功返回MyFile指针,失败返回NULL*/
MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;// 根据模式设置打开标志if(strcmp(mode, "w") == 0)       // 写模式{flag = O_CREAT | O_WRONLY | O_TRUNC; // 创建文件/只写/清空内容fd = open(path, flag, 0666);  // 设置文件权限为rw-rw-rw-}else if(strcmp(mode, "a") == 0)  // 追加模式{flag = O_CREAT | O_WRONLY | O_APPEND; // 创建文件/只写/追加fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0)  // 读模式{flag = O_RDWR;               // 读写模式fd = open(path, flag);}else{//TODO: 可以添加其他模式支持}// 检查文件是否成功打开if(fd < 0) return NULL;// 创建并初始化MyFile对象return BuyFile(fd, flag);
}

功能分析

  • 支持三种基本文件模式:写("w")、追加("a")和读("r")

  • 使用系统调用open打开文件,获取文件描述符

  • 根据模式设置不同的打开标志:

    • "w"模式会清空文件内容

    • "a"模式会在文件末尾追加

    • "r"模式允许读写

  • 最终调用BuyFile创建并初始化MyFile对象

3. 文件关闭函数 MyFclose

/*** @brief 关闭文件并释放资源* @param file MyFile对象指针*/
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;  // 检查文件描述符是否有效MyFFlush(file);      // 确保缓冲区数据写入文件close(file->fileno); // 关闭文件描述符free(file);          // 释放MyFile结构体内存
}

功能分析

  • 首先刷新缓冲区,确保所有数据写入文件

  • 关闭底层文件描述符

  • 释放MyFile结构体内存

  • 包含安全检查,防止无效文件描述符

4. 数据写入函数 MyFwrite

/*** @brief 向文件写入数据* @param file MyFile对象指针* @param str 要写入的数据指针* @param len 要写入的数据长度* @return 总是返回0(实际实现可能需要改进)*/
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 将数据拷贝到缓冲区memcpy(file->outbuffer + file->bufferlen, str, len);file->bufferlen += len;  // 更新缓冲区长度// 2. 检查是否满足刷新条件if((file->flush_method & LINE_FLUSH) &&      // 如果是行缓冲模式file->outbuffer[file->bufferlen-1] == '\n') // 且最后一个字符是换行符{MyFFlush(file);  // 刷新缓冲区}return 0;  // 注意:实际应该返回写入的字节数,这里需要改进
}

功能分析

1、memcpy(file->outbuffer + file->bufferlen, str, len);

memcpy(file->outbuffer + file->bufferlen, str, len);
  • file->outbuffer:指向文件输出缓冲区的起始地址。

  • file->bufferlen:当前缓冲区中已存储的数据长度(单位:字节)。

  • file->outbuffer + file->bufferlen:计算缓冲区中下一个空闲位置的地址(即已存数据的末尾)。

  • str:要写入的数据源地址(用户传入的字符串或二进制数据)。

  • len:要拷贝的数据长度(单位:字节)。

  • memcpy:标准内存拷贝函数,将 str 的 len 字节数据复制到目标地址。

2、按位与(&)运算的作用

  • LINE_FLUSH 的定义(在头文件中):

    #define LINE_FLUSH (1 << 1)  // 即二进制 0b10(十进制 2)
  • file->flush_method 是一个整型变量,存储当前的缓冲模式(可能是 NONE_FLUSHLINE_FLUSH 或 FULL_FLUSH 的组合)。

运算规则

  • 按位与 & 会对两个数的 二进制位 逐位比较:

    • 如果某一位在两个数中都是 1,结果的该位才是 1,否则是 0

  • 判断逻辑

    • 如果 file->flush_method 包含 LINE_FLUSH,则 file->flush_method & LINE_FLUSH 结果非零(true

    • 如果不包含,则结果为 0false)。

  • 将数据直接拷贝到输出缓冲区

  • 实现了行缓冲逻辑:当检测到换行符时自动刷新

  • 当前实现总是返回0,实际应该返回写入的字节数

  • 缺少缓冲区满时的处理逻辑(全缓冲模式)

5. 缓冲区刷新函数 MyFFlush

/*** @brief 刷新缓冲区,将数据写入文件* @param file MyFile对象指针*/
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;  // 缓冲区为空则直接返回// 将数据从用户缓冲区写入内核缓冲区int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;  // 忽略返回值(实际实现应该检查)fsync(file->fileno);  // 确保数据写入磁盘file->bufferlen = 0;  // 重置缓冲区长度
}

功能分析

  • 将缓冲区内容写入底层文件描述符

  • 使用fsync确保数据持久化到磁盘

  • 重置缓冲区长度为0

  • 当前实现忽略了write的返回值,实际应该处理写入错误情况

1、缓冲区空检查

if(file->bufferlen <= 0) return;
  • 作用:如果缓冲区中没有数据,直接返回

  • 优化意义:避免不必要的系统调用

  • 潜在问题:没有检查 file 是否为 NULL(健壮性考虑)

2、数据写入系统调用

int n = write(file->fileno, file->outbuffer, file->bufferlen);
(void)n;  // 忽略返回值
  • write 系统调用

    • 参数1:文件描述符

    • 参数2:要写入的数据缓冲区

    • 参数3:要写入的字节数

    • 返回值:实际写入的字节数(可能小于请求的字节数)

  • 问题

    • 完全忽略了 write 的返回值,可能导致部分写入未被处理

    • 没有错误处理(如 write 返回 -1 表示出错)

3、数据同步到磁盘

fsync(file->fileno);
  • fsync 系统调用

    • 强制将内核缓冲区中的数据写入物理磁盘

    • 确保数据持久化,不会因系统崩溃而丢失

  • 性能影响

    • 这是一个昂贵的操作,会显著降低 I/O 性能

    • 通常只在需要强一致性保证时使用

4、重置缓冲区

file->bufferlen = 0;
  • 作用:将缓冲区长度重置为0,表示缓冲区已空

  • 实现方式

    • 只是简单地重置长度计数器

    • 没有清空缓冲区内容(可能的安全问题)


三、Makefile

# 生成动态链接库 libmyc.so,依赖 mystdio.o 和 mystring.o 两个目标文件
# $@ 表示目标文件(libmyc.so),$^ 表示所有依赖文件
libmyc.so: mystdio.o mystring.ogcc -shared -o $@ $^# 编译 mystdio.c 生成位置无关代码(PIC)的目标文件
# -fPIC 选项是生成动态库所必需的
# $< 表示第一个依赖文件(mystdio.c)
mystdio.o: mystdio.cgcc -fPIC -c $<# 编译 mystring.c 生成位置无关代码(PIC)的目标文件
mystring.o: mystring.cgcc -fPIC -c $<# 伪目标:打包输出库文件
.PHONY: output
output:# 创建发布目录结构mkdir -p lib/include    # 存放头文件的目录mkdir -p lib/mylib      # 存放动态库的目录# 拷贝所有头文件到include目录cp -f *.h lib/include# 拷贝动态库文件到mylib目录cp -f *.so lib/mylib# 将整个lib目录打包压缩tar czf lib.tgz lib# 伪目标:清理生成的文件
.PHONY: clean
clean:# 删除所有目标文件、动态库文件和生成的目录rm -rf *.o libmyc.so lib lib.tgz

四、Makefile-st

# 生成静态链接库 libmyc.a,依赖 mystdio.o 和 mystring.o 两个目标文件
# 注意:与动态库(.so)不同,静态库(.a)在编译时会被完整嵌入可执行文件
# $@ 表示目标文件(libmyc.a),$^ 表示所有依赖文件
libmyc.a: mystdio.o mystring.o# 使用 ar 命令打包目标文件生成静态库# -r 替换现有文件 -c 创建新库ar -rc $@ $^# 编译 mystdio.c 生成目标文件
# 注意:静态库不需要 -fPIC 位置无关代码选项(与动态库不同)
# $< 表示第一个依赖文件(mystdio.c)
mystdio.o: mystdio.cgcc -c $<# 编译 mystring.c 生成目标文件
mystring.o: mystring.cgcc -c $<# 伪目标:打包发布静态库文件
.PHONY: output
output:# 创建标准的库发布目录结构mkdir -p lib/include    # 存放头文件(.h)的目录mkdir -p lib/mylib      # 存放静态库文件(.a)的目录# 拷贝所有头文件到include目录cp -f *.h lib/include# 拷贝静态库文件到mylib目录(注意这里是.a文件而非.so)cp -f *.a lib/mylib# 将整个lib目录打包压缩,便于分发tar czf lib.tgz lib# 伪目标:清理生成的文件
.PHONY: clean
clean:# 删除编译过程中生成的所有中间文件# 包括:目标文件(.o)、静态库文件(.a)、打包目录和压缩包rm -rf *.o libmyc.a lib lib.tgz

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90111.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90111.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90111.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zipformer

Zipformer首先&#xff0c;Conv-Embed 将输入的 100Hz 的声学特征下采样为 50 Hz 的特征序列&#xff1b;然后&#xff0c;由 6 个连续的 encoder stack 分别在 50Hz、25Hz、12.5Hz、6.25Hz、12.5Hz 和 25Hz 的采样率下进行时域建模。除了第一个 stack 外&#xff0c;其他的 st…

SpringMVC快速入门之请求与响应

SpringMVC快速入门之请求与响应一、请求处理&#xff1a;获取请求参数1.1 普通参数获取&#xff08;RequestParam&#xff09;1.1.1 基础用法1.1.2 可选参数与默认值1.2 路径变量&#xff08;PathVariable&#xff09;1.3 表单数据绑定到对象1.3.1 定义实体类1.3.2 绑定对象参数…

【Mysql】 Mysql zip解压版 Win11 安装备忘

1. 官网 MySQL :: MySQL Community Downloads 选择 MySQL Community Server 选择Archives 选择 8.0版本 MySQL :: Download MySQL Community Server (Archived Versions) 1. 普通版本&#xff08;推荐&#xff09; 名称&#xff1a;Windows (x86, 64-bit), ZIP Archive 文件…

Web3面试题

1.在使用 Ethers.js 对接 MetaMask 钱包时&#xff0c;如何检测用户账户切换的情况&#xff1f;请简述实现思路。 答案&#xff1a;可通过监听accountsChanged事件来检测。当用户切换账户时&#xff0c;MetaMask 会触发该事件&#xff0c;在事件回调函数中可获取新的账户地址&…

uni-app动态获取屏幕边界到安全区域距离的完整教程

目录 一、什么是安全区域&#xff1f; 二、获取安全区域距离的核心方法 三、JavaScript动态获取安全区域距离 1. 核心API 2. 完整代码示例 3. 关键点说明 四、CSS环境变量适配安全区域 1. 使用 env() 和 constant() 3. 注意事项 五、不同平台的适配策略 1. H5 端 2…

ZKmall开源商城微服务架构实战:Java 商城系统的模块化拆分与通信之道

在电商业务高速增长的今天&#xff0c;传统单体商城系统越来越力不从心 —— 代码堆成一团、改一点牵一片、想加功能得大动干戈&#xff0c;根本扛不住高并发、多场景的业务需求。微服务架构却能破这个局&#xff1a;把系统拆成一个个能独立部署的小服务&#xff0c;每个服务专…

ROS 与 Ubuntu 版本的对应关系

ROS 作为一套用于构建机器人应用的开源框架&#xff0c;其开发和运行高度依赖 Ubuntu 等 Linux 发行版&#xff0c;尤其是 Ubuntu 因其广泛的兼容性和社区支持&#xff0c;成为了 ROS 最主流的运行平台。 一、ROS 与 Ubuntu 版本的对应关系&#xff08;截至 2025 年&#xff0c…

GPT-4o mini TTS:领先的文本转语音技术

什么是 GPT-4o mini TTS&#xff1f; GPT-4o mini TTS 是 OpenAI 推出的全新一代文本转语音&#xff08;TTS&#xff09;技术&#xff0c;能够以自然、流畅的方式将普通文本转换为语音。依托先进的神经网络架构&#xff0c;GPT-4o mini TTS 在语音合成中避免了传统 TTS 的生硬…

Git下载全攻略

目标读者初学者或有经验的开发者不同操作系统用户&#xff08;Windows、macOS、Linux&#xff09;下载前的准备确认系统版本和位数&#xff08;32-bit/64-bit&#xff09;检查网络环境是否稳定确保有足够的磁盘空间Windows系统下载Git访问Git官方网站&#xff08;https://git-s…

ADAS域控软件架构-网络管理状态与唤醒机制

1. 状态介绍: Sleep Mode:总线睡眠模式,控制器不发送应用报文和网络管理报文。 Pre-Sleep Mode:准备总线睡眠模式,控制器不发送应用报文和网络管理报文。 Ready Sleep Mode:就绪睡眠模式,系统发送应用报文但是不发送网络管理报文。 Normal Operation mode:正常工作模式…

pytest简单使用和生成测试报告

目录 1. 基本使用 1--安装 2--pytest书写规则 3--为pycharm设置 以 pytest的方式运行 4--setup和teardown 5--setup_class和teardown 2. pytest生成测试报告 基本使用 安装 pytest文档地址 pytest documentation pip install pytest点击pycharm左边的控制台按钮 输入pip inst…

Spring Boot 第一天知识汇总

一、Spring Boot 是什么&#xff1f;简单说&#xff0c;Spring Boot 是简化 Spring 应用开发的框架 —— 它整合了整个 Spring 技术栈&#xff0c;提供了 “一站式” J2EE 开发解决方案。核心优点&#xff1a;快速创建独立运行的 Spring 项目&#xff0c;无需繁琐配置&#xff…

MySql主从部署

MySql主从部署 1、操作环境 硬件环境&#xff1a;香橙派5 aarch64架构 软件环境&#xff1a;Ubuntu 22.04.3 LTS 软件版本&#xff1a;mysql-8.0.42 操作方式&#xff1a;mysql_1,mysql_2容器 主节点&#xff1a;mysql_1 启动命令&#xff1a;docker run --name mysql_master \…

Redis——Redis进阶命令集详解(下)

本文详细介绍了Redis一些复杂命令的使用&#xff0c;包括Redis事务相关命令&#xff0c;如MULTI、EXEC、DISCARD 和 WATCH ,发布订阅操作命令&#xff0c;如PUBLISH 、SUBSCRIBE 、PSUBSCRIBE ,BitMap操作命令&#xff0c;如SETBIT、GETBIT、BITCOUNT、BITOP&#xff0c;HyperL…

C#使用socket报错 System.Net.Sockets.SocketException:“在其上下文中,该请求的地址无效。

bind: 在其上下文中&#xff0c;该请求的地址无效。问题定位 程序中运行socket服务端程序时&#xff0c;绑定的IP地址无效&#xff0c;即请求的IP地址在你的机子上找不到。原因有以下几种可能&#xff1a; 1&#xff09;server端绑定的IP地址不是本机的IP地址。 2&#xff09;之…

计算机底层入门 05 汇编学习环境通用寄存器内存

2.3 汇编学习环境我们通过上一章笔记&#xff0c;得知 计算机好像 只会通过位运算 进行 数字的加法。 而机器语言的魅力就是 位运算&#xff0c;解析规则。它们也都是通过 电路 来进行实现的。这就是 计算机最底层的本质了&#xff01;&#xff01;&#xff01; 汇编语言 所谓的…

Java学习---Spring及其衍生(上)

在 Java 开发领域&#xff0c;Spring 生态占据着举足轻重的地位。从最初的 Spring 框架到后来的 SpringBoot、SpringMVC 以及 SpringCloud&#xff0c;每一个组件都在不同的场景下发挥着重要作用。本文将深入探讨这几个核心组件&#xff0c;包括它们的定义、原理、作用、优缺点…

LVGL应用和部署(个人开发嵌入式linux产品)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】随着经济越来越走向常态化发展&#xff0c;将来的公司基本是两个趋势&#xff0c;一个是公司越做越大&#xff0c;越来越趋向于垄断&#xff1b;另外…

CPU,减少晶体管翻转次数的编码

背景 以4比特为单位&#xff0c;共16个数。仔细思考状态转换过程中的晶体管翻转次数。 0000 0001&#xff0c;1 0010&#xff0c;2 0011&#xff0c;1 0100&#xff0c;3 0101&#xff0c;1 0110&#xff0c;2 0111&#xff0c;1 1000&#xff0c;4 1001&#xff0c;1 1010&…

LLM 中的 温度怎么控制随机性的?

LLM 中的 温度怎么控制随机性的? 在LLM的解码过程中,温度(Temperature)通过调整token概率分布的“陡峭程度”来控制随机性:温度越低,概率分布越陡峭(高概率token的优势越明显),随机性越低;温度越高,分布越平缓(高低概率token的差异被缩小),随机性越高。 温度,…