【内存拷贝函数:memcpy与memmove】

2023-11-14


前言

c和c++使用的内存拷贝函数,memcpy函数和memmove函数的功能都是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

要使用这两个库函数需要引用头文件 <string.h>
函数原型:
void* memcpy(void* destination,const void* source,size_t num);
voidmemmove(void destination,const void* source,size_t num);
destination:目的地指针(首地址)
source:源头指针(首地址)
num:需要复制的字节数

不过两者又略有差异,下面就让我们一起来一探究竟。


一、memcpy函数(内存拷贝)的实验

代码如下:

#include<stdio.h>
#include<string.h>

int main() {
	int arr1[6] = { 0 };
	int i = 0;
	puts("请输入6个整数:");
	for (i = 0; i < 6; i++) {
		scanf("%d", arr1 + i);
	}
	int arr2[6] = {0};
	memcpy(arr2, arr1, sizeof(arr1[0]) * 6);
	for (i = 0; i < 6; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n");

	puts("请输入字符串:");
	char arr11[60] = { 0 };
		scanf(" %[^\n]", arr11);
	char arr22[60] = { 0 };
	memcpy(arr22, arr11, sizeof(arr11[0]) * 60);
		printf("%s", arr22);
	printf("\n");
	return 0;
}

运行示例:


二、memcpy函数的模拟实现

上面我们已经介绍了函数原型, void memcpy(void destination,const void* source,size_t num);
我们在模拟实现时对于数据的类型当然要保持一致,
memcpy有三个参数,分别是目的地指针、源头指针以及需要复制的字节数,
肯定有朋友会感到疑惑:怎么这里接收指针时使用的是void* ?
这里我们说了:这是一个内存函数,是直接对内存进行操作的,
所以,不管内存中的数据是什么类型的我都直接进行复制,
因此,这里使用void进行接收,因为void就像一个“垃圾桶”,什么类型的指针都可以放进去,
当然,既然是“垃圾桶”,那么里面的东西当然就是“不可再次使用的啦,”
嗯~,除非趁别人不注意时将它“捡起来重新洗干净”,
也就是说在使用 void指针进行运算时需要 强制类型转换,
因为 void* 不能和 char* 或者 int* 那样知道自己具体的大小,所以需要我们来提前告诉它。**

代码如下:

#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* e1, const void* e2, size_t num) {
	assert(e1 && e2);
	//断言函数,判断指针e1,e2是否为NULL
	//需要包含头文件<assert.h>
	void* ret = e1;
	while (num--) {
		*(char*)e1 = *(char*)e2;
		e1 = (char*)e1 + 1;
		e2 = (char*)e2 + 1;
		//我们是对字节进行操作的,所以强转为char*
		//((char*)e1)++;在部分编译器上可能跑不过去
	}

	return ret;
}

int main() {
	int arr1[6] = { 0 };
	int i = 0;
	puts("请输入6个整数:");
	for (i = 0; i < 6; i++) {
		scanf("%d", arr1 + i);
	}
	int arr2[6] = {0};
	my_memcpy(arr2, arr1, sizeof(arr1[0]) * 6);
	for (i = 0; i < 6; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n");

	puts("请输入字符串:");
	char arr11[60] = { 0 };
		scanf(" %[^\n]", arr11);
	char arr22[60] = { 0 };
	my_memcpy(arr22, arr11, sizeof(arr11[0]) * 60);
		printf("%s", arr22);
	printf("\n");
	return 0;
}

运行示例:
在这里插入图片描述

这里我们可以看到,memcpy函数十分方便,不管是什么类型的数据都可以进行复制,
但是,如果当我们要在一个数组内进行复制的时候,如果dest与src有重叠部分,那么使用memcpy就不一定会的得到我们想要的结果。

代码如下:

#include<stdio.h>
#include<assert.h>

void* my_memcpy(void* e1, const void* e2, size_t num) {
	assert(e1 && e2);
	void* ret = e1;
	while (num--) {
		*(char*)e1 = *(char*)e2;
		e1 = (char*)e1 + 1;
		e2 = (char*)e2 + 1;
	}

	return ret;
}

int main() {
	int arr1[10] = { 0 };
	int i = 0;
	puts("请输入10个整数:");
	for (i = 0; i < 10; i++) {
		scanf("%d", arr1 + i);
	}
	my_memcpy(arr1 + 2, arr1, sizeof(arr1[0]) * 5);
	for (i = 0; i < 10; i++) {
		printf("%d ", arr1[i]);
	}
	printf("\n");

	int arr2[10] = { 0 };
	puts("请输入10个整数:");
	for (i = 0; i < 10; i++) {
		scanf("%d", arr2 + i);
	}
	my_memcpy(arr2, arr2 + 2, sizeof(arr2[0]) * 5);
	for (i = 0; i < 10; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n");
	return 0;
}

运行实例:

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述
通过上图的变化路程可知:memcpy总是从低地址开始往高地址复制的,
但是当dest>src,并且目的地与源头有重叠时,使用memcpy会覆盖掉源头后面的数据,导致结果出错。
这个时候就需要进行判断,如果再次出现上面的事件,我们应当从高地址往低地址开始复制,
但是memcpy函数在设计时没有这种判断,
那么,我们就要使用其他的内存拷贝函数进行复制了,
下面我们来介绍memmove函数(终于引了出来,嘎嘎努力!!)。


三、memmove函数(内存移动)的实验

代码如下:

#include<stdio.h>
#include<string.h>

int main() {
	int arr1[10] = { 0 };
	int i = 0;
	puts("请输入10个整数:");
	for (i = 0; i < 10; i++) {
		scanf("%d", arr1 + i);
	}
	memmove(arr1 + 2, arr1, sizeof(arr1[0]) * 5);
	for (i = 0; i < 10; i++) {
		printf("%d ", arr1[i]);
	}
	printf("\n");

	int arr2[10] = { 0 };
	puts("请输入10个整数:");
	for (i = 0; i < 10; i++) {
		scanf("%d", arr2 + i);
	}
	memmove(arr2, arr2 + 2, sizeof(arr2[0]) * 5);
	for (i = 0; i < 10; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n");
	return 0;
}

运行示例:
在这里插入图片描述


四、memmove函数的模拟实现

**这里的memmove函数就完美解决了memcpy函数的“设计缺陷”
(其实memcpy设计时本就是只需要用它解决不同内存区域的内存拷贝的,而memmove是专门解决重叠内存区域内存拷贝的,大家各司其职,并没有高下之分。)
那么下面我们就来模拟实现memmove函数吧。

代码如下:

#include<stdio.h>
#include<assert.h>

void* my_memmove(void* e1, const void* e2, size_t num) {
	assert(e1 && e2);
	void* ret = e1;
	if (e1 < e2) {
		int i = 0;
		while (num--) {
			*((char*)e1 + i) = *((char*)e2 + i);
			i++;
		}
	}
	else {//如果dest>=src,就倒着复制
		while (num--) {
			*((char*)e1 + num) = *((char*)e2 + num);
		}
	}
	return ret;
}

int main() {
	int arr1[10] = { 0 };
	int i = 0;
	puts("请输入10个整数:");
	for (i = 0; i < 10; i++) {
		scanf("%d", arr1 + i);
	}
	memmove(arr1 + 2, arr1, sizeof(arr1[0]) * 5);
	for (i = 0; i < 10; i++) {
		printf("%d ", arr1[i]);
	}
	printf("\n");

	int arr2[10] = { 0 };
	puts("请输入10个整数:");
	for (i = 0; i < 10; i++) {
		scanf("%d", arr2 + i);
	}
	memmove(arr2, arr2 + 2, sizeof(arr2[0]) * 5);
	for (i = 0; i < 10; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n");
	return 0;
}

运行示例:
在这里插入图片描述


总结

以上就是memcpy和memmove的内容,
这里有两件值得一提的事情:
1是现在在很多编译器下memcpy的功能已经和memmove一样了,也就是说memcpy也可以进行重叠内存区域的拷贝。
2是这两个函数其实和strcpy与strncpy十分十分相似,只不过后两者是仅限字符串使用而已。
那么今天的内容就讲解到了这里,感谢大家的支持,欢迎大家来评论区一起探讨,大家的鼓励是在这里插入图片描述
继续更新的巨大动力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【内存拷贝函数:memcpy与memmove】 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 无限循环与无限递归。两者都是未定义的吗?

    无副作用的无限循环是未定义的行为 看here https coliru stacked crooked com view id 24e0a58778f67cd4举个例子参考参数 https en cppreference com w cpp
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 空指针与 int 等价

    Bjarne 在 C 编程语言 中写道 空指针与整数零不同 但 0 可以用作空指针的指针初始值设定项 这是否意味着 void voidPointer 0 int zero 0 int castPointer reinterpret cast
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐

  • 第五章 创建自定义窗口部件

    对已经存在的Qt窗口进行子类化或者直接对QWidget子类化可以快速创建自己的自定义窗口部件 一 自定义窗口部件 十六进制的QSpinBox 本来QSpinBox仅支持十进制数据的 现在子类化接收并显示十六进制数值 头文件 hexspinb
  • 【Flutter入门教程】从零构建电商应用(一)

    在这个系列中 我们将学习如何使用google的移动开发框架flutter创建一个电商应用 本文是flutter框架系列教程的第一部分 将学习如何安装Flutter开发环境并创建第一个Flutter应用 并学习Flutter应用开发中的核心概
  • 图像区域特征

    以 Halcon 里支持的 Region 特征为基础 做概念总结 形状特征 1 圆度 Circularity 衡量一个形状接近圆的程度 取值为 0 1 Circularity 区域面积 外接圆半径2 Circularity frac 213
  • High-Fidelity Pose and Expression Normalization for Face Recognition in the Wild

    CVPR 2015 Matlab code http www cbsr ia ac cn users xiangyuzhu projects HPEN main htm 中科院关于 人脸图像预处理 姿态和表情的归一化 算法的整体流程图如下所
  • crictl使用总结

    crictl 是 CRI 兼容的容器运行时命令行接口 crictl 是 CRI 兼容的容器运行时命令行接口 你可以使用它来检查和调试 Kubernetes 节点上的容器运行时和应用程序 crictl 和它的源代码在 cri tools 代码
  • 常用的Buck型DC-DC的原理图电路

    常用DC DC buck原理图电路 下图是比较完整的DC DC电路设计 全文将主要介绍各个元件的作用 针对该电路各位号分析 1 Vin的C1 C2主要是滤波 使得DC DC芯片输入能够得到较为干净的电 2 R1 R2是限流用的 一般是K级的
  • 2.信号和槽

    MyPushBotton mybtn new MyPushBotton mybtn gt setParent this mybtn gt setText 我自己的按钮 mybtn gt move 200 200 mybtn gt show
  • 数据结构 图的应用

    文章目录 生成树 定义 性质 带权图的最小生成树 最小生成树的生成规则 最小生成树 Kruskal算法 步骤 最小生成树 Prim算法 步骤 最短路径 非负权值的单源最短路径 Dijkstra算法 目的 算法 存储空间 算法步骤 算法实现
  • [Python人工智能] 十.Tensorflow+Opencv实现CNN自定义图像分类案例及与机器学习KNN图像分类算法对比

    从本专栏开始 作者正式开始研究Python深度学习 神经网络及人工智能相关知识 前一篇详细讲解了gensim词向量Word2Vec安装 基础用法 并实现 庆余年 中文短文本相似度计算及多个案例 很幸运被CSDN推荐至封面 本篇文章主要通过T
  • centos8在线文档编辑ONLYOFFICE安装

    第一件事情肯定是安装docker拉 这个在centos8上有点不一样 好在执行代码都一毛一样 root localhost dnf install y podman 上次元数据过期检查 2 02 46 前 执行于 2022年03月31日 星
  • Vue3+TS项目中element-plus自动导入组件后,找不到文件

    问题 原因 从报错代码来看 这是一个ts错误 而且是找不到名称 是没有将 d ts文件加入到tsconfig json配置文件中 所以Typescript还不认识它们 解决 找到项目根目录下 tsconfig json配置文件 includ
  • varchar与char的区别

    1 定长 VS 变长 char表示定长 长度固定 varchar表示变长 即长度可变 当插入数据的长度超过定义的长度时 如果数据库是严格模式 则会拒绝插入并提示错误信息 如果是宽松模式 则会截取然后插入 如果插入的字符串长度小于定义长度时如
  • mac如何彻底卸载Conda

    1 由于Anaconda的安装文件都包含在一个目录中 所以直接将该目录删除即可 删除整个Anaconda目录2 使用Anaconda Clean工具删除 安装Anaconda Clean 在终端输入并回车 conda install ana
  • 基于模拟退火算法与遗传算法的避障路径规划问题求解(Matlab代码)

    基于模拟退火算法与遗传算法的避障路径规划问题求解 Matlab代码 在本文中 我们将探讨如何使用模拟退火算法 Simulated Annealing 和遗传算法 Genetic Algorithm 相结合来解决避障路径规划问题 我们将提供相
  • Meta推出Code Llama:编程的未来已来

    今天 Meta推出了一项前沿技术 Code Llama 这是一款基于Llama 2构建的大型语言模型 LLM 专门用于生成和讨论代码 这一创新技术的发布标志着编程领域的新时代 Code Llama的亮点 先进的LLM Code Llama是
  • 穿特步的程序员教你如何找女朋友

    生活中我们常常发现很多程序员拿着高薪 却常常沦为单身狗 每当情人节来临 却只能形单影只的一个人 过得十分凄惨 自从程序员毕业出来工作进入 IT 行业之后 常常接触不到女性 一不小心就到了被催恋催婚的年纪 前有阿里的高级工程师 穿着一双特步的
  • 不平衡数据分类算法介绍与比较

    介绍 在数据挖掘中 经常会存在不平衡数据的分类问题 比如在异常监控预测中 由于异常就大多数情况下都不会出现 因此想要达到良好的识别效果普通的分类算法还远远不够 这里介绍几种处理不平衡数据的常用方法及对比 符号表示 记多数类的样本集合为L 少
  • 跟着(肆十二)学习——Yolov5训练自己的目标检测模型

    目录 1 环境配置 1 1安装Pycharm 1 2安装anaconda 1 3安装所需要的包 1 3 1安装pytorch 1 3 2安装其他依赖包 1 3 3pycocotools的安装 1 3 4测试一下 2 数据标注 2 1直接在代
  • 阮一峰老师的ES6入门:Generator 函数的语法

    Generator 函数的语法 1 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案 语法行为与传统函数完全不同 本章详细介绍 Generator 函数的语法和 API 它的异步编程应用请看 Generator
  • 【内存拷贝函数:memcpy与memmove】

    文章目录 前言 一 memcpy函数 内存拷贝 的实验 二 memcpy函数的模拟实现 三 memmove函数 内存移动 的实验 四 memmove函数的模拟实现 总结 前言 c和c 使用的内存拷贝函数 memcpy函数和memmove函数