使用C语言实现文件的拷贝——底层内存分析

news2024/11/26 15:41:24

使用C语言实现文件的拷贝

本文主要涉及sprintf()函数的讲解以及系统IO与标准IO的区别和一个实例使用C语言实现文件的拷贝,在最后还深度刨析了文件拷贝的底层原理。

文章目录

  • 使用C语言实现文件的拷贝
    • 一、 sprintf()函数
      • 1.1 sprintf ()函数的参数解析
      • 1.2 sprintf()函数将整数和浮点数格式化为字符串
    • 二、 系统 I/O(Input/Output)和标准 I/O
    • 三、 C语言实现文件的拷贝
      • 3.1 实现效果要求
      • 3.2 实现代码
      • 3.3 实现效果
      • 3.4 实现文件拷贝的底层内存刨析

一、 sprintf()函数

1.1 sprintf ()函数的参数解析

sprintf() 是 C 语言中的一个标准库函数,用于将格式化的数据写入字符串中。它的功能与 printf() 相似,但是 printf() 会将格式化的数据写入标准输出(通常是屏幕),而 sprintf() 则将数据写入一个字符串。

sprintf() 的原型如下:

#include <stdio.h>

int sprintf(char *str, const char *format, ...);

其中:

  • str:是一个字符数组或字符指针,用于存储格式化的输出字符串。在调用 sprintf() 之前,str 应该已经分配了足够的空间来存储生成的字符串,以防止缓冲区溢出。

  • format:是一个字符串,它包含了一个或多个格式说明符,用于指定如何格式化输出的数据。

sprintf() 函数与 printf() 函数在格式说明符和用法上是相同的,但是它不输出到标准输出,而是将结果写入 str 指定的字符串中。

1.2 sprintf()函数将整数和浮点数格式化为字符串

以下是一个 sprintf() 的简单示例:

#include <stdio.h>

int main() {
    char buffer[100];
    int num = 12345;
    float fnum = 67.89;

    // 使用 sprintf 将整数和浮点数格式化为字符串
    sprintf(buffer, "Integer: %d, Float: %.2f", num, fnum);

    // 打印生成的字符串
    printf("Formatted string: %s\n", buffer);

    return 0;
}

在上述示例中,sprintf() 将整数 num 和浮点数 fnum 格式化为一个字符串,并存储在 buffer 中。然后,我们使用 printf() 打印生成的字符串。

需要注意的是,为了安全起见,当使用 sprintf() 时,应确保目标字符串(即第一个参数 str)有足够的空间来存储格式化后的字符串,以防止缓冲区溢出。

在这里插入图片描述

二、 系统 I/O(Input/Output)和标准 I/O

系统 I/O(Input/Output)和标准 I/O 都是在计算机编程中用于数据输入和输出的概念,但它们之间存在一些区别。以下是对这两者的简要说明:

  1. 系统 I/O(Low-Level I/O)

    • 系统 I/O 指的是直接使用操作系统提供的系统调用(system calls)进行输入/输出操作。
    • 这种 I/O 是直接的、底层的,通常涉及到操作系统的内核级别。
    • 例如,在UNIX/Linux系统中,系统调用如 readwrite 用于从文件描述符(file descriptors)进行读写。
    • 系统 I/O 更接近底层硬件操作,通常提供了更细粒度的控制,但使用它可能需要处理更多的细节和复杂性。
  2. 标准 I/O(Standard I/O or Buffered I/O)

    • 标准 I/O 是在系统 I/O 之上构建的高级 I/O 概念。
    • 它使用了缓冲区(buffer)来优化数据的读写,减少了直接调用系统调用的频率,从而提高了性能。
    • 在C语言中,标准 I/O 是通过库函数实现的,如 stdio.h 提供的 fopen, fclose, fread, fwrite, fprintf, fscanf 等函数。
    • 标准 I/O 提供了对流(stream)的概念,如文件流、标准输入流(stdin)、标准输出流(stdout)等。
    • 标准 I/O 通常更易于使用和管理,因为它隐藏了底层的细节和复杂性。

总结

  • 系统 I/O 是直接的、底层的 I/O 操作,通常使用系统调用来进行读写,提供了更细粒度的控制。
  • 标准 I/O 是基于系统 I/O 构建的高级 I/O 抽象,使用缓冲区来优化数据的读写,提供了更简洁、易于使用的接口。

在实际编程中,通常建议使用标准 I/O,除非有特定的需求或优化目标需要使用系统 I/O。标准 I/O 提供了足够的性能,并且更易于管理和维护。

三、 C语言实现文件的拷贝

3.1 实现效果要求

使用C语言实现cp函数一样的效果,进行文件的拷贝。

执行要求:
执行:./a.out ./1.txt …/2.txt
执行代码./a.out 后将当前文件下的./1.txt 拷贝到 …/2.txt

3.2 实现代码

#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <source_file> <destination_file>\n", argv[0]);
        return 1;
    }

    const char *file1 = argv[1];
    const char *file2 = argv[2];

    // 打开源文件
    int fd_source = open(file1, O_RDONLY);
    if (fd_source == -1) {
        perror("Error opening source file");
        return 1;
    }

    // 获取源文件的权限
    struct stat stat_source;
    if (fstat(fd_source, &stat_source) == -1) {
        perror("Error getting source file status");
        close(fd_source);
        return 1;
    }

    // 创建或打开目标文件
    int fd_dest = open(file2, O_WRONLY | O_CREAT | O_TRUNC, stat_source.st_mode);
    if (fd_dest == -1) {
        perror("Error opening destination file");
        close(fd_source);
        return 1;
    }

    // 读取源文件并写入目标文件
    char buffer[1024];
    ssize_t bytes_read;
    while ((bytes_read = read(fd_source, buffer, sizeof(buffer))) > 0) {
        if (write(fd_dest, buffer, bytes_read) != bytes_read) {
            perror("Error writing to destination file");
            close(fd_source);
            close(fd_dest);
            return 1;
        }
    }

    // 关闭文件描述符
    close(fd_source);
    close(fd_dest);

    printf("File copied successfully.\n");

    return 0;
}


read(fd_source, buffer, sizeof(buffer)):从 fd_source 文件描述符中读取数据,将读取的数据存储在 buffer 中,最多读取 buffer 的大小(即 1024 字节)。

如果 read 调用成功并读取了数据,bytes_read 将存储实际读取的字节数

当 read 返回0(表示已到达文件末尾)或者返回一个错误(read 返回 -1)时,循环将结束

3.3 实现效果

执行命令:
在这里插入图片描述

执行效果:
在这里插入图片描述

3.4 实现文件拷贝的底层内存刨析

当你从底层内存角度考虑文件复制操作时,实际上涉及到数据在计算机系统中的存储和处理方式。以下是从底层内存的角度详细解释文件拷贝的过程和原因:

  1. 数据的存储

    • 文件数据存储在硬盘或其他存储设备上的磁盘块中。
    • 当你打开一个文件进行读取时,操作系统将这些磁盘块的内容读入内存的页缓存中。这样做是为了加速后续的读取操作,因为内存的读写速度远高于磁盘。
  2. 系统调用与缓冲

    • 使用openread系统调用时,操作系统会处理数据的缓冲和调度,确保数据被有效地从磁盘读入内存。
    • 同样,使用write系统调用时,操作系统会确保数据从内存写回到磁盘。
  3. 数据的传输

    • 在执行文件复制时,数据首先从源文件的内存缓存中被读取到应用程序的地址空间(通常是通过read系统调用实现的)。
    • 然后,这些数据从应用程序的地址空间被写入到目标文件的内存缓存中(通过write系统调用)。
    • 最终,操作系统会确保目标文件的内存缓存中的数据被写回到磁盘上的相应位置。
  4. 性能考虑

    • 文件拷贝操作在内存和磁盘之间进行,这通常比直接在磁盘之间进行文件拷贝更快。因为内存操作的速度远快于磁盘操作。
    • 此外,操作系统和硬件通常有各种优化,如预读、预写、缓冲等,进一步提高了数据传输的效率。
  5. 数据的完整性和一致性

    • 在数据传输过程中,操作系统会确保数据的完整性和一致性。这意味着,除非操作系统确定数据已经被完全写入目标文件,否则不会认为写操作已经完成。

总之,从底层内存的角度看,文件复制涉及数据在内存和磁盘之间的传输,操作系统在其中扮演了关键的角色,确保数据的正确、高效地传输。这也解释了为什么直接在磁盘之间执行文件拷贝通常比先将数据加载到内存中再写回更加高效。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java版直播商城免 费 搭 建:电商、小程序、三级分销及免 费 搭 建,平台规划与营销策略全掌握

随着互联网的快速发展&#xff0c;越来越多的企业开始注重数字化转型&#xff0c;以提升自身的竞争力和运营效率。在这个背景下&#xff0c;鸿鹄云商SAAS云产品应运而生&#xff0c;为企业提供了一种简单、高效、安全的数字化解决方案。 鸿鹄云商SAAS云产品是一种基于云计算的软…

Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读

文章目录 概述ObjectEncoderObjectDecoder Code源码分析ObjectEncoderObjectDecoder 小结 概述 Netty是一个高性能、异步的网络应用程序框架&#xff0c;它提供了对TCP、UDP和文件传输的支持。在Netty中&#xff0c;数据的发送和接收都是以字节流的形式进行的&#xff0c;因此需…

从实践角度优化数据库设计:深入解析三范式的应用

总述 第一范式(1NF):要求关系模式中的每个属性都是不可分的数据项,即属性具有原子性。第二范式(2NF):在满足1NF的基础上,要求关系模式中的所有非主属性都完全函数依赖于整个候选键(或主键)。第三范式(3NF):在满足2NF的基础上,要求关系模式中的每个非主属性都不传…

LVS最终奥义之DR直接路由模式

1 LVS-DR(直接路由模式) 1.1 LVS-DR模式工作过程 1.客户端通过VIP将访问请求报文&#xff08;源IP为客户端IP&#xff0c;目标IP为VIP&#xff09;发送到调度器 2.调度器通过调度算法选择最适合的节点服务器并重新封装数据报文&#xff08;将源mac地址改为调度器的mac地址&am…

centos(linux)安装jenkins

官网&#xff1a;https://pkg.jenkins.io/redhat/ 安装官网进行操作&#xff1a; sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.reposudo rpm --import https://pkg.jenkins.io/redhat/jenkins.io-2023.key若出现如下错误&#xff1a; …

JavaScript基础函数+对象+继承

目录 1.创建函数 2.函数分类 2.1带参数函数 2.2匿名函数 2.3嵌套函数 2.4立即执行函数 ES6特有的箭头函数 2.5对象中的函数 3.this对象 4.有参构造函数创建对象 5.原型 prototype 6.函数应用&#xff08;继承&#xff09; 6.1原型链继承 6.2构造继承 6.3组合继承&…

Observability:捕获 Elastic Agent 和 Elasticsearch 之间的延迟

在现代 IT 基础设施的动态环境中&#xff0c;高效的数据收集和分析至关重要。 Elastic Agent 是 Elastic Stack 的关键组件&#xff0c;通过促进将数据无缝摄取到 Elasticsearch 中&#xff0c;在此过程中发挥着至关重要的作用。 然而&#xff0c;显着影响此过程整体有效性的关…

vue3使用mock模拟后端接口

安装mock axios yarn add mock yarn add axios 新建在src/mockdata/automenu.js 模拟后端的json数据格式 import Mock from mockjs Mock.mock(/menu,get,{status: 200,menuList: [{id : 1,iconCls: "fa fa-window",name: 系统管理,url: /},{id: 2,icon: icon-j…

智能优化算法应用:基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.龙格-库塔算法4.实验参数设定5.算法结果…

CSS操纵元素的禁用和启用

通常表单控件都会有属性readonly、disabled对元素进行只读、禁用等操作。 而有时候我们想要div也达到类似效果&#xff0c;可以用CSS样式pointer-events: none进行控制。 科普知识 CSS样式的pointer-events: none用于控制一个元素能否响应鼠标操作。当该属性设置为none时&am…

【Docker-4】Docker 命令

1、镜像管理命令 docker images #查看本机镜像 [rootdocker-0001 ~]# docker imagesdocker search 镜像名称 #从官方仓库查找镜像 [rootdocker-0001 ~]# docker search busybox #需要联网&#xff0c;本次不用操作docker pull 镜像名称:标签 #下载镜像 [rootdocke…

MySQL基本操作 DDL DML DQL三大操作介绍

DDL 数据(结构)定义 创建表DML 数据操作 增删改DQL 查询语句 DDL 数据(结构)定义 创建表 创建 删除数据 注释 --空格内容 创建数据库 CREATE DATABASE [if not exists] 数据库名 [ CHARSET utf8]eg:CREATE DATABASE IF NOT EXISTS school CHARSET utf8如果对应school不存在,…

【GitHub精选项目】IP 地址转地理位置:ip2region 完全指南

前言 本文为大家带来的是 lionsoul2014 开发的 ip2region 项目&#xff0c;一种高效的离线 IP 地址定位库。ip2region 提供了10微秒级别的查询效率&#xff0c;支持多种主流编程语言&#xff0c;是一种理想的 IP 定位解决方案。 这个开源项目可以实现 IP 地址到地理位置的精确映…

2017年第六届数学建模国际赛小美赛A题飓风与全球变暖解题全过程文档及程序

2017年第六届数学建模国际赛小美赛 A题 飓风与全球变暖 原题再现&#xff1a; 飓风&#xff08;也包括在西北太平洋被称为“台风”的风暴以及在印度洋和西南太平洋被称为“严重热带气旋”&#xff09;具有极大的破坏性&#xff0c;往往造成数百人甚至数千人死亡。   许多气…

SVN搭建指导

环境 centos 7.9 SVN安装方式一&#xff1a;yum 1.1 http服务 至今还没有搞定网页版&#xff0c;网页版需要搭建apache http服务。遇到如下问题&#xff1a; centos - svn: Could not open the requested SVN filesystem - Stack Overflow 在试了加777权限&#xff0c;加a…

校园圈子交友系统,APP小程序H5,三端源码交付,支持二开!实名认证,大V认证,地图找伴,二手平台!

校园圈子交友系统&#xff0c;是属于自主定义开发的系统&#xff0c;内容有很多&#xff0c;先截取一些给大家看看&#xff0c;让大家更多的了解本系统&#xff0c;然后再做评价&#xff01; 校园后端下载地址&#xff1a;校园圈子系统小程序&#xff0c;校园拼车&#xff0c;校…

Pycharm 关闭控制台多余窗口详解(console)

文章目录 1 问题描述2 解决办法2.1 步骤1&#xff1a;编辑配置2.2 步骤2&#xff1a;使用 Python 控制台运行&#xff08;取消勾选&#xff09;2.3 验证&#xff1a;再次运行&#xff0c;多余窗口消失 1 问题描述 2 解决办法 2.1 步骤1&#xff1a;编辑配置 菜单路径&#xf…

【bug日记】如何切换jdk版本,如何解决java和javac版本不一致

背景 今天在安装jenkins后&#xff0c;使用java运行war包的时候&#xff0c;提示jdk1.8版本太低&#xff0c;需要提高版本&#xff0c;所以就需要切换jdk版本 解决 在用户变量中&#xff0c;首先更改了JAVA_HOME的地址为17的目录&#xff0c;发现javac的版本改为17了&#x…

云原生扫盲篇

What 云原生加速了应用系统与基础设施资源之间的解耦,向下封装资源以便将复杂性下沉到基础设施层;向上支撑应用,让开发者更关注业务价值 云原生是一种构建和运行应用程序的方法,也是一套技术体系和方法论. Cloud 表示应用程序位于云中而不是传统的数据中心Native表示应用程序从…

函数帧栈的创建和销毁(一)

目录 什么是函数栈帧 理解函数栈帧能解决什么问题 函数栈帧的创建和销毁 什么是栈 认识相关寄存器和汇编指令 相关寄存器 相关汇编命令 esp和ebp 解析函数栈帧的创建和销毁 学前补充 函数的调用堆栈 什么是函数栈帧 我们在写C 语言代码的时候&#xff0c;经常会把…