【memcpy和memove函数的详解】

news2024/11/27 7:38:57

1.memcpy函数详解

  • 模拟实现memcpy函数

2.memmove函数详解

  • 模拟实现memmove函数

memset函数详解

memcpy函数

了解一个函数,就查询该函数的相关信息

memcpy函数在库中的声明如下:

void * memcpy ( void * destination, const void * source, size_t num );

memcpy函数是将源头的字符串拷贝num个字节的数据到目标字符串,并返回一个void*的指针,也就是返回目标空间的首地址。

它的含义是:

Copy block of memory

在这里插入图片描述
我们知道,strcpy函数是拷贝字符串的,但是它并不能拷贝例如整型,结构体之类的东西,strcpy有一定的局限性,memcpy函数可以说涵盖了所有类型数据的拷贝。

下面直接看一个例子:

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1+2, sizeof(int)*5);
	//可以拷任意位置的数据,任意字节个数
	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", arr2[i]);
	}
	return 0;
}

在上面的例子中,memcpy是将arr1+2处开始的20个字节拷贝到arr2中,即拷贝3,4,5,6,7
我们可以看一下结果:

在这里插入图片描述

既然是一个个字节地拷贝,我们在模拟实现的时候,应该考虑到void的指针应该是转换成char ,才能够满足每个字节地拷贝。

memcpy函数模拟实现

上面说到,memcpy函数是一个个字节地拷贝,拷贝num个字节,我们模拟实现的时候,应该按照库函数的声明来模拟实现。

在这里插入图片描述

来看上图,将src中的每一个字节的数据都拷贝到dest中,最后返回dest的首地址,就完成了。

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest&&src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		//dest = (char*)dest + 1;
		//src = (char*)src + 1;
		++(char*)dest;
		++(char*)src;
		/*(char*)dest++;
		(char*)src++;*/  // error,先++,再强转,void*指针无法++
		//((char*)dest)++;
		//((char*)src)++;
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1+2, sizeof(int)*5);
	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", arr2[i]);
	}
}

首先应该断言,判断指针的有效性,既然库中的memcpy函数会返回目的地的首地址,所以应该设置一个void*的指针来保存该地址,因为在拷贝的过程中,目的地的地址会不断变化,在上面模拟实现的过程中,注释的几个代码都是可以实现的。

在这里插入图片描述
结果如上图所示:

不知道你是否想过一个问题,对于memcpy函数而言,假如我想要拷贝自己,假如把自己的3,4,5,6,7拷贝到1,2,3,4,5中,能不能实现呢?

就我现在模拟实现的函数而言,是无法实现的,因为在拷贝的过程中,自身的值会发生改变,也就变成了1 2 1 2 1 2 1 8 9 10
在这里插入图片描述
那么,针对这一问题,就有一个函数能够解决:memmove函数

memmove函数

memmove函数其实是更有针对性地对于有重叠数据的拷贝而诞生的。

就像上面的例子,假如拷贝的数据有重叠,就需要用到memove函数。

void * memmove ( void * destination, const void * source, size_t num );

函数声明与memcpy函数相同。

还是上面的例子,使用memmove能够完美实现拷贝重叠部分。

int main()
{
	int arr1[20] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	mmemmove(arr1+2, arr1, sizeof(int) * 5);
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", arr1[i]);
	}

结果如下:
在这里插入图片描述

那么,memmove函数的工作原理到底是什么呢?

在上面的例子中,不管目标位置是否大于原位置,memcpy拷贝的时候都是从前往后拷贝,这才导致拷贝不成功,但是当我们从后往前拷贝呢?

在这里插入图片描述
可以看到,当dest的位置大于src位置时,从后往前拷贝是没有问题的,那么假如dest的位置小于src的位置呢?
是否还是一直从后往前拷贝?
一直从后往前拷贝过于绝对。

当dest的位置小于src的位置时,从前往后拷贝是合适的。

在这里插入图片描述
当dest>src,并且超出了src的范围呢?

在这里插入图片描述
就像这样,其实,这种情况已经是不重叠的情况了,也就是使用memcpy的方式处理,我们在拷贝的时候,必须保证目标空间足够大,所以不可能存在越界拷贝的情况。

所以在这第三种情况的时候,无论从前往后拷贝,还是从后往前拷贝,都是可以的。
我们不如从后往前拷贝,这样就使问题简单多了。

所以我们得出结论

当dest<src -->从前往后拷贝
当dest>src,从后往前拷贝

图解如下:

在这里插入图片描述

下面就可以开始模拟实现了:

memmove函数模拟实现

有了上面的理解,模拟实现就好办了。

void* my_memmove(void* dest, const void* src,size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		// 前->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			((char*)dest)++;
			((char*)src)++;
		}
	}
	else // 后->前拷贝 
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num); //首先要找到最后一个字节
				//不用count -1,进入循环时,count已经--了
		}
	}
	return ret;
}

memset函数

void * memset ( void * ptr, int value, size_t num );

该函数的含义是:
在这里插入图片描述
该函数是一个设置函数,将指定的内存设置成指定的值。

什么意思呢?
举个例子:

int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 5);
	memset(arr+6, 'y', 5);
	printf("%s\n", arr);
	return 0;
}

该函数的意思是:将arr中的前 5 个字节设置成x,将arr的第6个字节开始的后五个字节设置成y。
注意:单位是字节,也就是一个一个字节地设置。
在这里插入图片描述

看下面的例子:

int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 40);
	return 0;
}

这样设置,会不会让arr中的10个元素都设置成1呢?

不会。
在这里插入图片描述
注意:单位是字节,是一个个字节地设置。

在这里插入图片描述

将16进制转换成十进制,与上图结果相符。

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

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

相关文章

Node.js安装与编写

Node.js是JavaScript运行环境&#xff0c;是可以让JavaScript运行在服务端的开发平台。 Node.js对一些特殊用例进行优化&#xff0c;提供替代的API。 Node.js本质上是为文件系统、数据库之类的资源提供接口。 Node.js是单线程的&#xff0c;通过事件循环&#xff08;event l…

05【JSP-MVC】

文章目录05【JSP-MVC】一、JSP简介1.1 JSP概述1.1.1 HTML和Servlet的弊端1.1.1 什么是JSP1.2 JSP体验&#xff1a;1.2.2 JSP的执行过程1.2.2 JSP和Servlet是什么关系&#xff1f;二、JSP的脚本元素2.1 JSP中的注释&#xff1a;2.2 JSP脚本表达式2.3 JSP代码片段2.4 JSP声明2.5 …

数字音频接口DAI之I2S/PCM

I2S/PCM数字音频接口概述数字音频接口DAI&#xff0c;即Digital Audio Interfaces&#xff0c;顾名思义&#xff0c;DAI表示在板级或板间传输数字音频信号的方式。相比于模拟接口&#xff0c;数字音频接口抗干扰能力更强&#xff0c;硬件设计简单&#xff0c;DAI在音频电路设计…

深度学习——循环神经网络RNN(笔记)

循环神经网络RNN&#xff1a;对于序列模型的神经网络 1.回顾&#xff1a;潜变量自回归模型 使用潜变量ht总结过去的信息 ①xt跟当前的ht和x(t-1)相关 ②ht跟ht-1和xt-1相关 ③n元语法模型中&#xff0c;单词xt在时间t的条件概率取决于前面n-1个单词 隐藏层和隐藏状态的区别…

【ESP32Cam项目1】:ESP32Cam人脸检测(ArduinoESP32底层、Python版opencv)

人脸检测项目效果图&#xff1a; 人脸检测效果视频&#xff1a; 暮年的主页 - 抖音 (douyin.com) 人脸检测项目目标&#xff1a; 大家好&#xff01;近期拿到了便宜的ESP32Cam开发板&#xff0c;摄像头让我想起来人脸识别&#xff0c;于是ESP32Cam人脸检测项目由此诞生。后期还…

一文总结ACE代码框架

一、前言ACE_Engine框架是OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;的UI开发框架&#xff0c;为开发者提供在进行应用UI开发时所必需的各种组件&#xff0c;以及定义这些组件的属性、样式、事件及方法&#xff0c;通过这些组件可以方便进行OpenHarmony…

JavaScript面试题

目录1.★★ 介绍一下JS的内置类型有哪些&#xff1f;2.★★★★ 介绍一下 typeof 区分类型的原理3.★★★ 介绍一下类型转换4.★★★★ 说说你对 JavaScript 的作用域的理解。什么是作用域链&#xff1f;5.★★ 解释下 let 和 const 的块级作用域6.★★★★ 说说你对执行上下文…

用Vue+Nodejs+Axios+express连接Sqlserver做一个动态网页demo

文章目录一、主要工具二、流程2.1. 安装Node2.2. 新建Vue工程并启动2.3. 前后端通信2.3.1. 修改前端2.3.2. 用axios来发起请求2.3.3. 创建服务端程序一、主要工具 Vue做前端页面Nodejs做服务器后端&#xff08;Nodejs是一个JS的运行环境&#xff0c;可以让JS像其它后端语言一样…

使用超体素上下文和基于图的优化从MLS点云对城市地区的树木进行实例分割

Abstract 在本文中&#xff0c;开发了一种用于从城市场景中的 MLS 数据集中提取树木的实例分割方法。所提出的方法利用超体素结构来组织点云&#xff0c;然后从超体素的局部上下文中提取去除趋势的几何特征。结合局部上下文的去趋势特征&#xff0c;将采用随机森林&#xff08…

Qt基于CTK Plugin Framework搭建插件框架--创建插件

文章目录一、前言二、工程搭建2.1、新建Qt工程2.2、CTK环境配置三、CTK Plugin Framework使用3.1、主函数启动插件框架3.2、插件的创建3.3、插件的使用一、前言 CTK保姆级编译教程&#xff1a;https://blog.csdn.net/Mr_robot_strange/article/details/128547331?spm1001.201…

JavaGUI:多功能计算器(五)--Swing实现双语数据包+菜单切换(完整源码)

JavaGUI&#xff1a;多功能计算器&#xff08;五&#xff09;–Swing实现双语数据包菜单切换&#xff08;完整源码&#xff09; 【背景提示】 “软件国际化”就是实现多种语言显示切换。 1.在前端网页上&#xff0c;可通过探测本地化语言环境实现自动切换&#xff1b; 2.在操…

C++ 20 原子引用 (一)

C 20 原子引用 &#xff08;一&#xff09; std::atomic_ref{} std::atomic_ref类型对其引用的对象进行原子操作。 使用std::atomic_ref 进行多线程读写时不会造成数据争用。被引用对象的生命周期必须超过std::atomic_ref 。操作std::atomic_ref 的子对象是未定义行为。 错…

Docker版RabbitMQ安装延迟队列插件及延迟队列项目应用实战

前言 在项目中经常有延迟业务处理的背景&#xff0c;此时可以借助于Rabbitmq的延迟队列进行实现&#xff0c;但Rabbitmq本身并不支持延迟队列&#xff0c;但可以通过安装插件的方式实现延迟队列 环境准备 首先确认目前项目使用的Rabbitmq的版本&#xff0c;这里博主的版本是3.…

STM32系列(HAL库)——使用ESP8266-01S物联网模块连接Onenet云平台上报DHT11温湿度

前言 本篇主要讲解如何使用ESP8266-01S物联网模块连接Onenet云平台&#xff0c;并上报DHT11模块的温湿度数据。本文单片机主控采用STM32F405RGT6&#xff0c;使用其他主控的话基本要求有2个串口&#xff0c;一个串口用于调试使用&#xff0c;另一个用于ESP模块通讯。 一、前…

SOLIDWORKS 2023工程图和出详图新功能 创建更智能化 更高精度的工程详图

工程图是传达您设计意图的重要文档&#xff0c;您设计的产品越复杂&#xff0c;越需要详细注释说明。SOLIDWORKS 2023增强的工程图和出详图功能将帮助您创建更智能化、更高精度的工程详图&#xff0c;并且扩展新功能使您的设计工作延伸到更多的业务领域。您现在可以从更高层级的…

C 程序设计教程(15)—— 选择结构程序设计练习题

C 程序设计教程&#xff08;15&#xff09;—— 选择结构程序设计练习题 该专栏主要介绍 C 语言的基本语法&#xff0c;作为《程序设计语言》课程的课件与参考资料&#xff0c;用于《程序设计语言》课程的教学&#xff0c;供入门级用户阅读。 目录C 程序设计教程&#xff08;1…

20230110配置ubuntu18.04为开机自动登录

20230110配置ubuntu18.04为开机自动登录 百度搜索&#xff1a;ubuntu 18.04 开机自动登录 https://blog.csdn.net/yang1994/article/details/124446319 通过配置文件启用/禁用Ubuntu 18.04自动登录 超级用户可以通过编辑custom.conf配置文件的方式&#xff0c;来为自己或任何其…

计算机如何存储浮点数和定点数?

1 浮点数的不精确性 能不能用二进制表示所有实数&#xff0c;然后在二进制下计算它的加减乘除呢&#xff1f; 打开Chrome Console&#xff0c;输入0.3 0.6&#xff1a; 简单加法在js算出结果居然不是准确的0.9&#xff0c;而是0.8999999999999999&#xff0c;why&#xff1…

火山语音丨AI创作惊艳四方 诸多挑战仍在路上

2022年8月&#xff0c;一幅名为《太空歌剧院》的数字画作获得冠军同时引发了巨大争议&#xff0c;AIGC&#xff08;AI产生内容&#xff1a;AI-Generated Content&#xff09;出圈的事件便频频出现在大众视野。同年11月30日OpenAI发布的聊天机器人模型ChatGPT免费开放&#xff0…

从0到1完成一个Vue后台管理项目(二十、地图涟漪、线图)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…