内存函数:memcpy(拷贝),memmove(拷贝),memcmp(比较),memset(设置)

内存函数

  • 一.memcpy(内存拷贝1)
    • 1.函数使用
    • 2.模拟实现
  • 二.memmove(内存拷贝2)
    • 1.函数使用
    • 2.模拟实现
  • 三.memcmp(内存比较)
    • 1.函数使用
    • 2.模拟实现
  • 四.memset(内存设置)
    • 1.函数使用
    • 2.模拟实现

前言:

  • 之前我们学习了一些字符串库函数,并且模拟实现了它们。但是它们只能作用于字符串,那有没有适用于各种数据类型的函数呢?答案是有的,现在我将为你介绍——内存函数(memory function)
  • 内存函数头文件——string.h

一.memcpy(内存拷贝1)

1.函数使用

void* memory(void* destination, const void* source, size_t num);
  • memcpy函数用于拷贝两块独立空间中的数据(不可重叠),将源内存块按照每一个字节拷贝到目标内存块,num表示要拷贝字节的数目。destination 与 source 都是拷贝与被拷贝的起始地址。
#include<stdio.h>
#include<string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7 };int arr2[10] = { 0 };int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr2, arr1, 24);memcpy(arr3 + 2, arr3, 20);return 0;
}

在这里插入图片描述

  • 观察到arr1将前6个整形拷贝到了arr2中,也就是24个字节,是没有问题的。
  • 而第二个操作本来打算:将 arr3 中的 1,2,3,4,5 拷贝到 arr3 中的 3,4,5,6,7 上,但是得到的是1,2,1,2,1这是因为将1,2拷贝到3,4上,原来的内容就被修改了,达不到要求的效果。

总结:

  1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
  2. 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  3. 用于拷贝两块独立空间中的数据,如果source和destination有任何的重叠,复制的结果都是未定义的。
  4. 由于不知道拷贝什么样的数据,所以参数的指针类型为void*(可接收任意类型指针),在函数内部强制类型转换为 char*,可以用于每个字节拷贝。

2.模拟实现

  • 首先是必不可少的断言操作,其次就是要保留目标空间的起始地址便于之后的返回。然后拷贝每一个字节中的内容,所以要循环 num 次。每拷贝一个字节,两个指针向后移动,直到循环结束为止。拷贝结束后返回起始地址即可。
void* my_memcpy(void* destination, const void* source, size_t num)
{assert(destination && source);//断言,当二者之一指针为NULL则报错void* ret = destination;//存起始地址便于返回while (num--)//循环拷贝,每次拷贝一个字节{*(char*)destination = *(char*)source;destination = (char*)destination + 1;source = (char*)source + 1;}return ret;
}

注意:在对 destination 和 source 指针进行操作时,要先将它们强制类型转换为 char* 类型的指针。(char* 类型的指针解引用操作时向后访问一个字节的内容)。而 void* (泛型指针)是不能进行解引用操作的。

二.memmove(内存拷贝2)

1.函数使用

  • 上面学习了memcpy函数,得知其不能用于处理重叠的内存之间的数据拷贝,那么这样实现重叠部分的数据拷贝呢?memmove会带你实现。
void* memmove(void* destination, const void* source, size_t num);
  • memmove函数用于拷贝两块空间中的数据(可以重叠),将源内存块按照每一个字节拷贝到目标内存块,num表示要拷贝字节的数目。destination 与 source 都是拷贝与被拷贝的起始地址。
#include<stdio.h>
#include<string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1 + 2, arr1, 20);return 0;
}

在这里插入图片描述

总结:

  1. memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  2. 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

2.模拟实现

分析:
1.当要将紫色区间拷贝到蓝色区间,若采用从前往后(1,2,3,4,5)拷贝,会改变紫色区间中要进行拷贝的数据。换个思路:采用从后往前拷贝,能避免这个问题。
在这里插入图片描述

2.当要将紫色区间拷贝到蓝色区间,若采用从后往前(7,6,5,4,3)拷贝,会改变紫色区间中要进行拷贝的数据。换个思路:采用从前往后拷贝,能避免这个问题。

在这里插入图片描述
总结:

  1. 在同一块内存中:重叠时,当dest指针 > src指针,采用从前向后拷贝。
  2. 在同一块内存中:重叠时,当dest指针 < src指针,采用从后向前拷贝。
  3. 在同一块内存中:若没有重叠,二者都可以采用。
  4. 在不同块内存中:二者都可以采用。
void* my_memmove(void* destination, const void* source, size_t num)
{assert(destination && source);//断言void* ret = destination;//保存起始地址if (destination < source)//从前向后拷贝{while (num--){*(char*)destination = *(char*)source;destination = (char*)destination + 1;source = (char*)source + 1;}}else//从后向前拷贝{while (num--){*((char*)destination+num) = *((char*)source+num);}}return ret;//返回起始地址
}

三.memcmp(内存比较)

1.函数使用

int memcmp(const void* ptr1, const void* ptr2, size_t num);
  • memcmp函数用于比较两个内存块大小的函数。它会比较从 ptr1 和 ptr2 指针开始的第一个字节。当 *ptr1 大于 *ptr2 的时候返回一个大于0的数;当 *ptr1 等于 *ptr2 的时候返回0;当 *ptr1 小于 *ptr2 的时候返回一个小于0的数。比较完一次,ptr1 与 ptr2 指向下一个字节,循环 num 次,直到结束为止 。
#include<stdio.h>
#include<string.h>
int main()
{int arr1[] = { 1,257 };//01 00 00 00 01 01 00 00int arr2[] = { 1,2 };  //01 00 00 00 02 00 00 00int ret = memcmp(arr1, arr2, 8);printf("%d\n", ret);return 0;
}

在这里插入图片描述
在这里插入图片描述

  1. 在VS中内存采用的是小端存储方式。
  2. 数字在内存中是以2进制补码的形式存储的,但是为了更好的观察,以16进制展现出来。
  3. arr1与arr2中的前4个字节的内容都相同;但第5个字节,1<2,所以arr1 < arr2。

2.模拟实现

  • 首先就是断言,比较字节中的内容,若相同比较下一个字节中的内容,num–。直到字节内容不同或num==0,停止循环,返回二者字节内容的差值。
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{assert(ptr1 && ptr2);while (*(char*)ptr1 == *(char*)ptr2 && num--)//1.*(char*)ptr1 != *(char*)ptr2  2.num==0                {ptr1 = (char*)ptr1 + 1;ptr2 = (char*)ptr2 + 1;}if(num==0)//二者相同return 0;else//二者不同return  *(char*)ptr1 - *(char*)ptr2;
}

四.memset(内存设置)

1.函数使用

void* memset(void* destination, int value, size_t num);
  • memset函数可以将内存块的某一部分设置为特定的字符。三个参数中,第一个参数是开始设置内存的起始位置,第二个参数是要将内存设置成的字符,第三个参数是从起始位置开始需要设置的内存的字节数。memset函数设置内存的时候是一个字节一个字节地设置的。
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "hello world";memset(arr + 6, 'x', 5);printf("%s\n", arr);return 0;
}

在这里插入图片描述
再比如:将arr初识化为全1(每个字节都初始化为1)

#include<stdio.h>
#include<string.h>
int main()
{int arr[10] = { 0 };memset(arr, 1, 40);int i = 0;for (i = 0; i < 10; i++){printf("%d\n", arr[i]);}return 0;
}

在这里插入图片描述
因为改变一次改变一个字节,而不是所以不适合初始化整形数组。适合修改字符串内容。

2.模拟实现

void* my_memset(void* destination, int value, size_t size)
{assert(destination);void* ret = (char*)destination;//保留目标空间的起始地址便于之后的返回while (size--){*(char*)destination = value;destination = (char*)destination + 1;}return ret;
}

创作不易,如果能帮到你的话能赏个三连吗?感谢啦!!!
在这里插入图片描述

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

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

相关文章

【Linux】用户组、用户、文件权限(ugo权限),权限掩码,chmod,chown,suid,sgid,sticky,su,sudo

用户组 注意&#xff1a;普通用户只能查看有哪些组&#xff0c;不能创建/修改/删除&#xff0c;会提示&#xff1a;用户名 is not in the sudoers file.This incident will be reported. groupadd 用户组名新建用户组cat /etc/group查看有哪些组&#xff08;普通用户可以操作…

Windows下配置TortoiseGit 访问Ubuntu虚拟机下Samba共享目录

前言&#xff1a; 本文记录学习使用 Git 版本管理工具的学习笔记&#xff0c;通过阅读参考链接中的博文和实际操作&#xff0c;快速的上手使用 Git 工具。 本文参考了引用链接博文里的内容。 引用: 【TortoiseGit】TortoiseGit安装和配置详细说明-CSDN博客 Git版本管理可视…

Java——对象的打印

当我们运行如下代码&#xff1a; public class Person {String name;String gender;int age;public Person(String name,String gender,int age){this.name name;this.gender gender;this.age age;}public static void main(String[] args){Person person new Person(&quo…

Matlab读取数据文件

MATLAB可以读取很多类型的数据文件&#xff0c;包括文本文件、Excel文件、MAT文件等。以下是一些常见的数据读取函数&#xff1a; 文本文件&#xff1a; load 函数&#xff1a;用于读取MATLAB的二进制文件&#xff08;.mat文件&#xff09;。fscanf 函数&#xff1a;用于读取AS…

QT客户端开发的注意事项

QT客户端开发是一个涉及图形用户界面&#xff08;GUI&#xff09;设计、网络编程、数据库交互等多个方面的复杂过程。以下是在进行QT客户端开发时应注意的一些关键事项&#xff0c;通过关注这些事项&#xff0c;可以提高QT客户端应用的质量和开发效率。北京木奇移动技术有限公司…

Eclipse下载安装教程(包含JDK安装)【保姆级教学】【2024.4已更新】

目录 文章最后附下载链接 第一步&#xff1a;下载Eclipse&#xff0c;并安装 第二步&#xff1a;下载JDK&#xff0c;并安装 第三步&#xff1a;Java运行环境配置 安装Eclipse必须同时安装JDK &#xff01;&#xff01;&#xff01; 文章最后附下载链接 第一步&#xf…

微软推出的Microsoft Fabric 到底是什么?

近期&#xff0c;总有客户问小编&#xff0c;微软推出的 Microsoft Fabric 是什么&#xff1f;这个产品有什么特别之处呢&#xff1f;希望下面这篇文章能为大家解开一些疑惑。 微软Fabric是2023年5月推出的一个数据分析平台&#xff0c;它将关键数据管理和分析工作负载整合到一…

go语言控制goroutine协程退出的2种方法总结

我们知道&#xff0c;在go语言中&#xff0c;goroutine的执行会随着main线程的退出而终结&#xff0c; 即如果main线程退出&#xff0c;则所有的goroutine都会被强制退出&#xff0c;不管你是否已经执行完毕。 如果我们希望main进程等待所有的goroutine执行完毕后再退出&#…

【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)

&#x1f50d;目的 允许将新功能添加到现有的类层次结构中&#xff0c;而不会影响这些层次结构&#xff0c;也不会有四人帮访客模式中那样循环依赖的问题。 &#x1f50d;解释 真实世界例子 我们有一个调制解调器类的层次结构。 需要使用基于过滤条件的外部算法&#xff08;是…

奖金+1 万,OpenTenBase 开源核心贡献挑战赛,KB 专家助力其跑在 K8s 上

OpenTenBase 是由开放原子开源基金会孵化及运营的开源项目&#xff0c;是一款开放中立的企业级分布式 HTAP 开源数据库。OpenTenBase 具备高扩展性、商业数据库语法兼容、分布式 HTAP 引擎、多级容灾和多维度资源隔离等能力&#xff0c;已成功应用于金融、医疗、航天等行业的核…

FlyFlow:支持驳回后自动跨节点跳回

本周更新 新增&#xff1a;审批节点驳回&#xff08;拒绝配置的驳回&#xff09;支持自动跳回当前节点新增&#xff1a;修改数据节点新增&#xff1a;删除数据节点新增&#xff1a;子流程支持配置自动跳过发起人节点优化&#xff1a;两个项目合并一个单体项目优化&#xff1a;…

Hadoop阶段性技能抽检题,无直接答案但有提示信息

项目名&#xff1a;Hadoop平台及组件的部署管理 考核内容&#xff1a; 考核以大数据技术为核心内容&#xff0c;重点考查同学们基于Hadoop平台环境下&#xff0c;利用Hadoop技术生态组件&#xff0c;综合软件开发相关技术&#xff0c;解决实际问题的能力&#xff0c;所有学生在…

LeetCode hot100-38-Y

226. 翻转二叉树给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。这道题莫名其妙做出来了&#xff0c;看评论好多人都是莫名其妙做出来的。就是连续做了几道题有感觉了。很难解释。 我的做法 后序递归 class Solution {public TreeNode i…

LeetCode:2589.完成所有任务的最少时间(贪心 Java)

目录 完成所有任务的最少时间 题目描述&#xff1a; 实现代码与解析&#xff1a; 贪心 原理思路&#xff1a; 完成所有任务的最少时间 题目描述&#xff1a; 你有一台电脑&#xff0c;它可以 同时 运行无数个任务。给你一个二维整数数组 tasks &#xff0c;其中 tasks[i] …

Linux 系统下进程异常的处理方式

Linux 系统异常进程处理 一、僵尸进程 说明&#xff1a;僵尸进程对系统来说就是系统已经接管不了并处于异常状态的进程&#xff0c;既不会自动释放&#xff0c;也不能被系统接管&#xff0c;下面列出几种查看并kill僵尸进程的方式 。 方式一、使用如下命令查看目前系统状态为…

【C语言】水仙花数

问题 水仙花数&#xff08;Narcissistic number&#xff09;也被称为超完全数字不变数&#xff08;pluperfect digital invariant, PPDI&#xff09;、自恋数、自幂数或阿姆斯壮数数&#xff08;Armstrong number&#xff09;。 它是指一个n位数&#xff08;n≥3&#xff09;…

【C++】---继承

【C】---继承 一、继承的概念及定义1、继承的概念2、定义语法格式3、继承基类成员访问方式的变化 二、基类 和 派生类 的对象之间的赋值转换1、赋值规则2、切片&#xff08;1&#xff09;子类对象 赋值 给 父类对象&#xff08;2&#xff09;子类对象 赋值 给 父类指针&#xf…

Promise链式调用与错误处理

Promise链式调用是一种处理异步操作的方法&#xff0c;它可以依次执行多个异步任务&#xff0c;并且可以在每个任务完成后进行后续操作。 在Promise链式调用中&#xff0c;每个任务都返回一个Promise对象&#xff0c;可以通过调用.then()方法来指定任务完成后的操作&#xff0…

Python邮件处理库之flanker使用详解

概要 Flanker是一个开源的邮件处理库,专门设计用于解析、验证和构建电子邮件地址和MIME消息。由Mailgun开发,它旨在提高邮件处理的效率和准确性,尤其适用于需要高效邮件验证和解析的应用程序。 安装 安装Flanker非常简单,可以通过Python的包管理器pip进行安装: pip ins…