【C语言】memmove()函数(拷贝重叠内存块函数详解)
🦄个人主页:修修修也
🎏所属专栏:C语言
⚙️操作环境:Visual Studio 2022

目录
一.memmove()函数简介
1.函数功能
2.函数参数
1>.void * destination
2>.onst void * source
3>.size_t num
3.函数返回值
4.函数头文件
二.memmove()函数的具体使用
1.使用memmove()函数完成拷贝整型数组数据(目的地与源重叠)
2.使用memmove()函数完成拷贝字符数组数据(目的地与源重叠)
三.模拟实现memmove()函数功能
🎏实现思路
1.函数参数及返回值设定逻辑
📌函数参数:
📌函数返回值:
2.函数功能实现逻辑
1.情况一(两个内存块不重叠)
2.情况二(两个内存块有重叠)
🎏代码编写
🎏运行测试
1.测试my_memmove()函数的逆序拷贝
2.测试my_memmove()函数的顺序拷贝
结语
一.memmove()函数简介
我们先来看一下cplusplus.com - The C++ Resources Network网站上memmove()函数的基本信息:

1.函数功能
可以看到,memmove()函数的功能是:
从源头指向的内存块拷贝固定字节数的数据到目标指向的内存块,并且源头的内存块与目标内存块可以重叠.(最后一点是memmove()与memcpy最大的区别)
2.函数参数
该函数一共有三个参数,分别是:
void * memmove ( void * destination, const void * source, size_t num );
1>.void * destination
第一个参数的类型是无类型指针(void*),它指向拷贝的目的地内存块,它的作用是为函数提供目的地的内存块起始地址,以便函数能够准确地将内容拷贝到我们需要的内存空间.
2>.onst void * source
第二个参数的类型是被const修饰(const修饰的指针,const在*左边表示指针指向的内容不可修改,const在*右边表示指针的指向不可修改)的无类型指针(void*),它指向拷贝数据的来源内存块,它的作用是为函数提供拷贝源头内存块起始地址,以便函数能够准确找到拷贝的源头进行拷贝.
3>.size_t num
第三个参数的类型是size_t(无符号整形),它表示要拷贝数据的字节数,它的作用是告诉函数需要拷贝的字节数是多少,以便函数精准的拷贝该数目字节数空间的内容到目的地.
3.函数返回值
函数的返回值类型是无类型指针(void*),它的作用是在函数运行结束后返回拷贝后的目的地内存块的起始地址.
4.函数头文件
该函数包含在头文件<string.h>中.
二.memmove()函数的具体使用
memmove()函数的使用场景是:
当我们想拷贝一个整型数组/结构体/枚举常量等strcpy()函数无法拷贝的数据,并且目的地内存块和源头内存块可能会有重叠的时候,我们可以考虑使用memmove()函数来完实现这一诉求,当然,想要使用memmove()函数拷贝字符串数据或者拷贝目的地内存块和源头内存块不重叠也是可以的.(但是会有些杀鸡用牛刀的感觉哈哈哈)
下面是拷贝时源内存块与目标内存块重叠的情况示意图:

1.使用memmove()函数完成拷贝整型数组数据(目的地与源重叠)
因为拷贝目的地内存块与源内存块不重叠的情况我们已经在memcpy()函数部分详细展示过了,因此在memmove()函数部分我们将着重展示它的内存块重叠时的使用情况.
如下,我们使用memmove()函数将arr数组中的1,2,3,4,5拷贝到3,4,5,6,7的位置上去:
分别给memmove()函数传入三个参数:
拷贝目的地地址(即arr+2),拷贝来源地址(即arr),拷贝字节数(即sizeof(arr[0])*5).
/* memmove 使用测试 */
#include <stdio.h>
#include <string.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr+2, arr, sizeof(arr[0])*5); //sizeof(arr[0])计算的结果是arr数组中一个元素的字节大小,乘5代表5个for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
在vs编译器中运行查看结果:

可以看到memmove()函数成功的将arr数组中的1,2,3,4,5拷贝到了3,4,5,6,7的位置上.
2.使用memmove()函数完成拷贝字符数组数据(目的地与源重叠)
如下,我们使用memmove()函数将str数组的" very useful"拷贝到" useful......"的位置上去:
分别给memmove()函数传入三个参数:
拷贝目的地地址(即str+20),拷贝来源地址(即str+15),拷贝字节数(即11).
/* memmove 使用测试 */
#include <stdio.h>
#include <string.h>int main()
{char str[] = "memmove can be very useful......";memmove(str + 20, str + 15, 11);//使用memmove()函数拷贝内存块重叠字符串printf("%s\n", str);return 0;
}
在vs编译器中运行查看结果:

可以看到memmove()函数成功的将str数组的" very useful"拷贝到了" useful......"的位置上.
三.模拟实现memmove()函数功能
🎏实现思路
1.函数参数及返回值设定逻辑
📌函数参数:
void * destination
因为memmove()函数要实现的是内存空间的拷贝,所以在使用memmove()函数时我们难免会遇到拷贝不同类型数据的可能,因此在这里我们需要将目的地的地址类型设置为无类型指针(void*),以便函数后续可以处理任意类型的数据.
const void * source
将来源地址的类型设置为无类型指针(void*)的原因与目的地的原因相同,都是便于函数可以处理任意类型的数据.
而给来源的地址指针加上const的原因是防止拷贝的过程中将来源的内容不慎修改,在*指针左侧加上const就可以使const修饰的指针指向的内容变成常量.
size_t num
因为要拷贝的字节数恒为非负数,因此字节数的类型是无符号整形(size_t).
📌函数返回值:
void*
函数返回值设置为void*的原因同目的地及来源地相同,都是便于函数可以在处理完任意类型的数据后可以返回目的地的地址.
2.函数功能实现逻辑
在讲实现逻辑之前,我们先分情况讨论一下在拷贝数据时,我们可能遇到的几种情况:
注:以下演示中,*src指针指向源头起始内存块,*dest指针指向目标起始内存块.
1.情况一(两个内存块不重叠)
这种情况下内存空间的拷贝逻辑是较为简单的,不论是数据从前向后拷贝还是从后向前拷贝,结果都是正确的.
例如这种情况:

或是这种情况:

2.情况二(两个内存块有重叠)
这种情况就比较复杂了,同样分两种情况,如图:
当源头指针在前时,只能从源头内存块的后端向前端移动拷贝:

拷贝逻辑示意图(序号代表拷贝次序):

当目的地指针在前时,只能从源头内存块的前端向后端移动拷贝:
拷贝逻辑示意图(序号代表拷贝次序):

分析清楚上面四种不同的情况,我们就可以在代码的编写中对不同情况进行分情况讨论了.
当然这里的分情况是十分灵活的,你可以四种情况各分一种,也可以按拷贝模式来分,从前向后拷的算一种,从后向前拷的算另一种.
除了两个必须按固定方式拷贝的情况之外,剩下两种情况无论按哪种方式拷贝都行.
那么我们在这里就选择一种最简单也最容易理解的方式来模拟实现memmove()函数吧.
即如果源头指针在前(小),从后向前拷贝;目的地指针在前(小),从前向后拷贝
数据在内存中的存储示意图:

🎏代码编写
清楚了不同情况的拷贝逻辑后,代码的编写就只需要按上面说的分两种情况就好了,以及,函数在实现时的其他需要注意的点我都标注在注释中了.
综上,my_memmove()函数的完整实现代码如下:
//模拟实现my_memmove()函数void* my_memmove(void* destination, const void* source, size_t num)
{assert(destination); //防止源头或目的地指针为NULLassert(source);void* ret = destination;if (source < destination) //内存中数据的存储是由低地址到高地址的{//从后向前拷贝while (num--) //以num为20为例,在num进入while循环之后,就立刻--,变成19了{*((char*)destination + num ) = *((char*)source + num );//这时source+num刚好指向的是源头内存块的最后一个字节}}else{while (num--){//从前向后拷贝*((char*)destination) = *((char*)source);++((char*)destination); ++((char*)source);//这里使用后置++的话一定要给(char*)destination整体带上括号//否则后置++的优先级比(char*)的强制类型转换的优先级高,//导致指针类型还是void*时就进行++操作,这是在C标准中是不允许的}}return ret;
}
🎏运行测试
1.测试my_memmove()函数的逆序拷贝
如下,我们使用my_memmove()函数将arr数组的1,2,3,4,5拷贝到3,4,5,6,7的位置上去:
即按照前面提到的这种情况进行拷贝:
vs2022测试运行结果:

可以看到my_memmove()函数成功的将arr数组中的1,2,3,4,5拷贝到了3,4,5,6,7的位置上.
2.测试my_memmove()函数的顺序拷贝
如下,我们使用my_memmove()函数将arr数组的3,4,5,6,7拷贝到1,2,3,4,5的位置上去:
即按照前面提到的这种情况拷贝:
vs2022中测试运行结果:

可以看到my_memmove()函数成功的将arr数组中的3,4,5,6,7拷贝到了1,2,3,4,5的位置上.
结语
希望这篇memmove()函数的介绍到能对大家有所帮助,欢迎大佬们留言或私信与我交流.
最后的最后,感谢这位大佬指出了我在memcpy()函数阶段模拟实现的不足,因为和他的交流,才促成了这篇博客的产生:


学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!
相关文章推荐
【C语言】memcpy()函数
【C语言】memset()函数
【C语言】strcpy()函数
【C语言】strlen()函数
【C语言】rand()函数(如何生成指定范围随机数)
【实用调试技巧】总是找不到Bug?手把手教你在vs2022中调试程序

C语言内存相关库函数思维导图:

相关文章:
【C语言】memmove()函数(拷贝重叠内存块函数详解)
🦄个人主页:修修修也 🎏所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.memmove()函数简介 1.函数功能 2.函数参数 1>.void * destination 2>.onst void * source 3>.size_t num 3.函数返回值 4.函数头文件 二.memmove()函数…...
04-流媒体-ffmpeg.c源码分析
ffmpeg.c是一个使用ffmpeg库的参考代码,实现了视频格式转换的功能,类似于我们常用的格式工产,源代码的的目录是: ffmpeg-4.2.2/fftools/ffmpeg.c 和前面的ffplay一样,我们分析其源代码,主要只是为了让读者了解ffmpeg.c此文件的大概流程,并且熟悉常用的ffmpeg库的API。 下…...
迭代器 Iterator
迭代器是一种设计模式,它用于遍历集合或容器中的元素,能够访问集合的元素而无需关心集合的内部结构: 特点: 封装集合访问:迭代器封装了对集合元素的访问,通过迭代器访问集合中的元素,而无需了…...
掌握CSS Flexbox,打造完美响应式布局,适配各种设备!
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 ⭐ 专栏简介 📘 文章引言 基…...
FlutterUnit 周边 | 收录排序算法可视化
theme: cyanosis 1. FlutterUnit 更新:排序算法可视化 排序算法可视化是用视图层表现出算法执行过程中排序的过程,感谢 编程的平行世界 在 《十几种排序算法的可视化效果,快来看看!👀》》 一文中提供的算法支持。我进行…...
代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
LeetCode T435 无重叠区间 题目链接:435. 无重叠区间 - 力扣(LeetCode) 题目思路: 这题思路和昨天的打气球类似,我们需要按照左区间或者右区间进行排序,然后哦判断第i个区间的左端点和第i-1个区间的右端点的大小关系,,如果大于等于,那么就无需操作,一旦…...
发展高质量存储力,中国高科技力量聚浪成潮
中国信息通信研究院指出,在全球数字化转型与产业变革的浪潮下,算力正在成为改变全球竞争格局的关键力量。而根据最新的《算力基础设施高质量发展行动计划》,算力是集信息计算力、数据存储力和网络运载力于一体的新型生产力。当前,…...
修改svc的LoadBalancer的IP引发的惨案
文章目录 背景修改externalIPs的操作api-server报错日志挽救教训 背景 k8s集群没有接外部负载均衡,部署istio的时候ingressgateway一直pending。 于是手动修改了这个lb svc的externalIP,于是k8s就崩了,如何崩的,且听我还道来。 …...
2520. 统计能整除数字的位数
2520. 统计能整除数字的位数 class Solution {public int countDigits(int num) {int res 0;int o num;while (num > 0) {if (o % (num % 10) 0) {res 1;}num num / 10;}return res;} }...
BeanUtils.copyProperties的用法
常见场景 我们如果有两个具有很多相同属性名的JavaBean对象a和b,想把a中的属性赋值到b,例如 接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象 传统做法是手动set,即 XxxQuery xxxQuer…...
【RabbitMQ 实战】12 镜像队列
一、镜像队列的概念 RabbitMQ的镜像队列是将消息副本存储在一组节点上,以提高可用性和可靠性。镜像队列将队列中的消息复制到一个或多个其他节点上,并使这些节点上的队列保持同步。当一个节点失败时,其他节点上的队列不受影响,因…...
PyCharm社区版安装
PyCharm社区版安装 到中国官网下载 https://www.jetbrains.com/zh-cn/pycharm/download/?sectionwindows 首次创建项目,会自动下载安装Python 3.9 社区版的区别 社区版的区别...
【LeetCode每日一题合集】2023.10.16-2023.10.22(只出现一次的数字Ⅲ)
文章目录 260. 只出现一次的数字 III⭐(异或)🐂2652. 倍数求和解法1——枚举模拟解法2—— O ( 1 ) O(1) O(1)容斥原理相似题目——1201. 丑数 III(二分查找容斥原理) 2530. 执行 K 次操作后的最大分数解法1——贪心优…...
尚硅谷大数据项目《在线教育之实时数仓》笔记003
视频地址:尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第7章 数仓开发之ODS层 P015 第8章 数仓开发之DIM层 P016 P017 P018 P019 01、node001节点Linux命令 02、KafkaUtil.java 03、DimSinkApp.java P020 P021 P022 P023 第7章 数…...
【Linux】部署单体项目以及前后端分离项目(项目部署)
一、简介 以下就是Linux部署单机项目和前后端分离项目的优缺点,希望对你有所帮助。 1、Linux部署单机项目: 优点: 1.简化了系统管理:由于所有服务都在同一台机器上运行,因此可以简化系统管理和维护。 2.提高了性能&a…...
设计模式之门面模式
前言 什么是门面模式 门面模式是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。它定义了一个高层接口,让子系统更容易使用。这种模式常用于将一个复杂的子系统封装成一个简单的接口,使得客户端可以方…...
Postman的使用
Postman的使用 Postman断言Postman常用断言1、断言响应状态码2、断言包含某个字符串3、断言JSON数据4、Postman断言工作原理 Postman关联Postman自动关联创建环境 3、Postman参数化CSV文件JSON文件1、用例集的导入导出2、环境导出 Postman断言 让Postman工具代替人自动判断预期…...
QGIS008:QGIS拓扑检查、修改及验证
摘要:本文介绍使用QGIS拓扑检查器和几何图形检查器检查图层的拓扑错误,修改拓扑错误,并对修改后的图层进行错误验证。 实验数据: 链接:https://pan.baidu.com/s/1Vy2s-KYS-XJevqHNdavv9A?pwdf06o 提取码:…...
安装DBD-Oracle报错处理
cd DBD-Oracle-1.83 perl Makefile.PL make && make install make编译报错如下: /bin/ld: 找不到 -lnsl collect2: 错误:ld 返回 1 make: *** [Makefile:524:blib/arch/auto/DBD/Oracle/Oracle.so] 错误 1 [rootlocalhost DBD-Ora…...
【机器学习】KNN算法-鸢尾花种类预测
KNN算法-鸢尾花种类预测 文章目录 KNN算法-鸢尾花种类预测1. 数据集介绍2. KNN优缺点: K最近邻(K-Nearest Neighbors,KNN)算法是一种用于模式识别和分类的简单但强大的机器学习算法。它的工作原理非常直观:给定一个新数…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...



