(memcpy memmove memcmp memset)内存操作函数详解

news2024/11/19 3:15:46

目录

    🍕注意:内存操作函数隶属于头文件,因此在使用任何内存操作函数之前都必须引用 

    🥞memcpy函数

    🍞memcpy函数的初步认识及使用

    🍳样例示范:

    🧈代码呈现: 

    🧀my_memcpy函数的实现

   🥗memcpy函数的分析

    🥙代码呈现

    🥪运行效果:

    🌮memmove函数

    🌯memmove函数的认识

    🍖代码呈现:

    🍗运行效果 

    🍠现象背后的本质

    🥠my_memmove的实现

    🥡代码呈现:

    🍱运行效果:

​编辑

    🍘memcmp 和 memset 函数的使用介绍

    🦪代码呈现:

    🍣运行结果: 


    🍿还记得我们之前所认识的 strcpy 函数吗?这个函数的作用是将一个字符数组中的内容拷贝到另一个字符数组当中。但是在我们了解并实现 strcpy 函数的时候就会很容易发现一个弊端,要是我们想要复制的内容不是一个字符数组该怎么办呀?要是一个整型数组或者是一个浮点数数组呢?那应该用什么函数呢?今天我们就来认识可以解决以上疑问的函数,内存操作函数。

    🍕注意:内存操作函数隶属于<string.h>头文件,因此在使用任何内存操作函数之前都必须引用<string.h> 

    🥞memcpy函数

    🍞memcpy函数的初步认识及使用

    🍿这个函数就是和我们 strcpy 函数相对应存在的,用于处理各种数据类型进行复制。我们先通过函数原型进行 memcpy 函数的基本了解与认识。

    🍿 由上图可以了解到我们的 memcpy 函数在使用的时候需要向函数中传递三个参数,第一个参数为目的地数组的指针,第二个参数为源头数组的指针(源数组->目的地数组 进行复制),第三个参数为一个无符号整型也就是说我们需要向函数说明本次复制的字节的个数。

    🍿由上面的函数接口我们可以了解到,memcpy 函数实质上是按照字节个数进行一个字节一个字节进行复制操作的。因此 memcpy 函数不仅可以实现数组的整体复制而且可以实行数组部分的复制。更大程度上增加了我们使用函数的灵活性。那么接下来我们就来通过一个使用样例感受一下 memcpy 函数使用的奥妙之处吧!

    🍳样例示范:

    🧈代码呈现: 

#include<string.h>
#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,0 };
	int arr2[] = { 12,13,14,15,16,17 };
	memcpy(arr1, arr2, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(arr1 + i));
	}
	return 0;
}

    🍿如同上图所示,我们将数组 arr2中的前五个元素(20个字节)复制进入数组 arr1 当中。 之后我们将 arr1 通过打印的形式展示出来之后发现数组 arr1 改变的只有前五个元素,其他元素均不变。经过上面的示例之后我相信大家已经明白该怎使用 memcpy 函数了,那么接下来就让我们来亲自实现以下这个 memcpy 函数进而加深我们的学习体会吧!

    🧀my_memcpy函数的实现

    🍿首先我们需要做的第一步就是逐步分析 memcpy 函数的操作原理,只有在了解了 memcpy 函数是如何执行复制操作之后才可以真正意义实现我们的 memcpy 函数。

   🥗memcpy函数的分析

    🍿对于 memcpy 函数,我们已经了解到了他其实是逐个字节依次进行赋值操作的。为了使得我们设计的函数同样可以适应多种数据的复制,所以我们的函数接口同样应该设计成 void * 类型。借鉴并仿照上面的函数原型我们同样可以将我们的函数接口设计成 void *类型,实质上是返回我们目的地数组首元素的指针。

    🍿有了以上的思路我们就可以正是着手实现我们的memecpy函数了。

    🥙代码呈现

#include<stdio.h>

//模拟实现memcpy
void* my_memcpy(void* dest, const void* sour, size_t size)
{
	void* ret = dest;
	while (size--)
	{
		*(char*)dest = *(char*)sour;
		dest = (char*)dest + 1;
		sour = (char*)sour + 1;
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,0 };
	int arr2[] = { 11,22,33,44,55,66,77,88,99,100 };
	my_memcpy(arr2, arr1, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(arr2 + i));
	}
	return 0;
}

    🥪运行效果:

    🍿由上面的代码我们可以看出,我们 my_memcpy 函数的实现的难点就在于类型的强转操作。为什么我们再赋值的时候要先进行强转操作才可以进行赋值呢?

    🍿这是因为:我们为了适应所有类型的数据的复制操作,所以我们需要将函数的类型定义成 void *类型的数据,但是为什么又要将 void *强转成为(char *)类型而不是其他的类型呢?这与我们函数接口中的传参有关,我们向函数中上传的第三个参数是一个以字节为单位的无符号整型,因此我们想要按照字节,一个字节一个字节的复制的话就只有 char 类型的数据符合条件。(同样的是为了满足我们使用者各种各样的需求,想象一下,假如将函数设计成强转为(int *)类型的数据的时候使用者想要复制四个字节的数据也是不可能的了,不是吗?)

    🍿上面我们的 my_memcpy 也就设计完成了。但是肯定会有小伙伴会突发奇想:要是将一个数组中的数据复制进入本数组中会出现什么呢?

    🍿假如我们想要将 1-5 这五个数据复制到 3-7 这五个数据的位置,按照我们的想象复制完成的数据本来应该是:1 2 1 2 3 4 5 8 9 0 。但是我们事实上的运行效果却是:1 2 1 2 1 2 1 8 9 0 。这是什么原因呢?

    🍿相信小伙伴们很容易就想到:元素覆盖 1->3 的位置的时候将 3 覆盖了。依次再次复制到 3 的位置的时候只能复制出 1 ,而不是 3 。那么又该怎么解决这种现象呢?这时候我就需要来向大家介绍下一个 memmove 函数了。

    🌮memmove函数

    🌯memmove函数的认识

    🍟我们要想在同一个数组中进行复制操作的时候就需要使用到一个新的函数—— memmove 函数了。同样的,我们先来分析函数原型,认识一下这个函数。

    🍟通过图片我们可以知道,memcpy 函数和 memmove 的函数不管是函数接口还是函数的返回值都是一样的,事实上,这两个函数的使用方法也确实是一模一样的。我们对其用法直接通过一段代码来体会:

    🍖代码呈现:

#include<stdio.h>
#include<string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	memmove(arr + 2, arr, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(arr + i));
	}
	return 0;
}

    🍗运行效果 

    🍟我们发现,使用我们的 memmove 函数并不会出现这个问题,这是什么原因呢?为了向大家解释着其中的奥秘,我们得先从出现这种现象的的原因进行讲起。

    🍠现象背后的本质

    🍟如图我们的 memcpy 函数从前向后一个字节一个字节的进行复制,3 和 4 等元素都被覆盖成了 1 和 2 这是我们运行结果出错的原因。但是有没有办法避免这种不必要的覆盖呢?或者说先覆盖我们不需要的元素呢?

 

    🍟我们只需要略微调换一下位置之后就会很容易就发现原来这么简单!我们可以先从我们最后一个元素进行覆盖,一直向前,那么即使我们源数组中的元素被覆盖了也没有任何影响,反正我已将用完了。不是吗?这样就我们的复制操作也就可以正常进行了。但是你又会发现,并不是所有单个数组中的情况都能用从后向前复制。 

    🍟假如我们想将 3-7 复制到我们的 1-5 的位置的时候从后向前复制的话反而会覆盖,从前向后复制则不会。那么我们就需要仔细观察了,那么什么时候才可以用从前向后复制呢?什么时候从后向前复制呢?经过我们仔细观察发现,我们可以将我们的源数组和我们的目的地数组的指针进行比较,如果源数组的指针大于目的地数组那么就是用从后向前的复制顺序,反之则使用从前向后的复制顺序。有了这一点经验之后我们就可以自主设计一个 memmove 函数了。

    🥠my_memmove的实现

    🍟经过上面的分析之后我们就已经了解过了 memmove 和 memcpy 之间的主要差别。我们就可以抓住这点差别来进行程序的编写。闲话少说,直接来看代码。

    🥡代码呈现:

#include<stdio.h>

//自主实现memmove
void* my_memmove(void* dest, void* sour, size_t size)
{
	void* ret = dest;
	if (sour>dest)
	{
		while (size--)
		{
			*(char*)dest = *(char*)sour;
			dest = (char*)dest + 1;
			sour = (char*)sour + 1;
		}
	}
	else
	{
		while (size--)
		{
			*((char*)dest + size) = *((char*)sour + size);
		}
	}
	return ret;
}

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

    🍱运行效果:

     🍟值得我们注意的同样是和 memcpy 函数中提到的一样:注意强制类型转换。那么之后让我们再来认识一下其他的内存操作函数吧!

    🍘memcmp 和 memset 函数的使用介绍

    🍤接下来就轮到介绍我们的 memcmp 函数和 memset 函数了。对于这两个函数均是和 memcpy 和 memmove 函数类似,对单个字节进行操作的函数。

    🍤首先,memcmp是将两个不同类型的元素一个字节一个字节的进行比较。如果相同则返回0,反之按照情况返回一个大于或小于0 的数字。(返回方案和strcmp类似)

     🍤就像是上图中显示的一样。只需要在最后加上一个参数(所要比较的字节的个数)即可。

     🍤之后对于我们的 memset 同样具有很重要的作用,该函数是对单个字节进行设置指定的值。

     🍤由上图我们可以了解到 memset 函数的第一个参数是一个 void*类型的指针,也就是我们需要设置的元素的地址,第二个元素为我们所要设置的元素的值,第三个元素是我们索要设置的字节的个数。

    🍤经过建议的了解之后我们来通过实例来体会这两个函数的使用方法。

    🦪代码呈现:

#include<stdio.h>
#include<string.h>
int main()
{
	float arr1[] = { 12.1,13.42 };
	float arr2[] = { 12.1,14.6 };
	int ret1 = memcmp(arr1, arr2, 4);
	int ret2 = memcmp(arr1, arr2, 5);
	printf("%d %d", ret1, ret2);
	return 0;
}
#include<stdio.h>
#include<string.h>
int main()
{
	int arr[5] = { 0 };
	memset(arr, 1, 4);
	int i = 0;
	printf("%x\n", arr[0]);
	return 0;
}

    🍣运行结果: 

 

     🍤由上图我们的运行展示也就结束了。由于后两个函数的自定义并不难所以就叫给大家自己来尝试啦。那么本次博客的内容到此结束,感谢大家的观看。祝大家天天开心。

 

 

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

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

相关文章

浏览器原理二三事

目录 1. 如何理解 JavaScript 是单线程的 2. 进程与线程 2.1 在浏览器中&#xff0c;如何理解进程和线程的关系&#xff1f; 2.2 浏览器的五种进程 2.2.1 浏览器主进程&#xff08;Browser 进程&#xff09; 2.2.2 浏览器渲染进程&#xff08;Renderer 进程&#xff09; …

论文投稿指南——中文核心期刊推荐(航空、航天2)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

Linux——【磁盘管理和LVM卷组管理】

前言&#xff1a; Linux 磁盘管理好坏直接关系到整个系统的性能问题&#xff0c;目前市场上的磁盘分类有&#xff1a;IDE磁盘&#xff08;多用于PC机&#xff09;、SATA磁盘、SAS磁盘、SSD磁盘等这么几种分类&#xff0c;企业中服务器大多为后面的两种&#xff0c;SATA磁盘多用…

二叉树11:完全二叉树的节点个数

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;222. 完全二叉树的节点个数 题目&#xff1a; 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数…

24行代码简单实现qq空间自动点赞

什么是Auto.js&#xff1f; Auto.js是基于JavaScript语言运行在Android平台上的工具。它依赖于无障碍服务。 它可以做什么&#xff1f; 解放双手&#xff0c;让手机自动打游戏、自动签到、自动领红包等等等等 它有什么优点&#xff1f; 不需要root权限&#xff08;Android…

Python pandas有几千个库函数,你用过几个?(3)

上一篇链接&#xff1a; Python pandas有好几百个库函数&#xff0c;你都用过吗&#xff08;2&#xff09;_Hann Yang的博客-CSDN博客 R(read_)&#xff1a; Function26~45 Types[Function][25:45] [read_clipboard, read_csv, read_excel, read_feather, read_fwf, read_gb…

目标检测算法——mmdetection下面的deformable-detr运行

1、环境 包版本mmcv-full1.4.2mmdet2.19.1torch1.10.0cu113torchvision0.11.1cu1132、文档 mmet官方文档 mmcv官方文档 源码下载 3、数据集 自定义数据集 4、修改代码 4.1、生成文件 打开mmdetection-master/tools下面执行train.py文件 其中配置文件--config在 mmdetec…

STM32中断使用NVIC

抢占优先级和响应优先级 值越小优先级越高&#xff0c;抢占优先级高就可以的打断抢占优先级低的&#xff1b;但同一个抢占优先级&#xff0c;高响应优先级并不能打断低响应优先级&#xff1b;当然中断同时发生的响应优先级高的先执行。简单理解为&#xff1a;抢占优先级能控制…

RV1126笔记十七:吸烟行为检测及部署<五>

若该文为原创文章,转载请注明原文出处。 ubuntu16.04上搭建转化成RKNN环境并把onnx转成RKNN模型(ubuntu16.04) onnx模型转rknn模型需要用到py3.8,所以搭建环境在转换,下面全部操作都是虚拟机下进行。 注意:转模型用py3.6, 训练用py3.8 一、miniconda安装 1、下载地址…

论文投稿指南——中文核心期刊推荐(自然科学总论)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

基于python的AD-census立体匹配算法实现

文章目录前言一、AD-census是什么&#xff1f;1.代价计算2.代价聚合3.视差优化4.视差后处理二、基于python的AD-census立体匹配算法实现前言 AD-Census算法来自于中国学者Xing Mei等在ICCV2011发表的论文《On Building an Accurate Stereo Matching System on Graphics Hardwar…

多类型随机图形生成方法及应用研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Linux-5 基础命令

Linux-5 基础命令 查看类命令 此类命令仅能查看文件中的内容 ls是用来查看目录中的内容cat是用来查看文件中的内容 查看文件 cat 选项 -n&#xff1a;显示文件内容的行数-A&#xff1a;显示文件中的特殊字符&#xff08;如果从Windows拷贝配置文件到Linux&#xff0c;很容易出…

化学试剂Biotin-PEG-COOH,Biotin-PEG-acid,生物素-聚乙二醇-羧基

英文名称&#xff1a;Biotin-PEG-COOH&#xff0c;Biotin-PEG-acid 中文名称&#xff1a;生物素-聚乙二醇-羧基 生物素-PEG-COOH是一种含有生物素和羧酸的线性杂双功能PEG试剂。它是一种有用的带有PEG间隔基的交联或生物结合试剂。生物素能以高特异性和亲和力与亲和素和链霉亲…

C++:STL:常用容器(上):string

1&#xff1a;string容器 1.1 string基本概念 本质&#xff1a; string是C风格的字符串&#xff0c;而string本质是一个类 string和char* 区别 1&#xff1a;char* 是一个指针 2&#xff1a;string是一个类&#xff0c;类内部封装了 char* 管理这个字符串&#xff0c;是一个 …

5G无线技术基础自学系列 | 5G网络接入问题分析

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 5G网络的接入失败或者接入时延过大都会…

实现安卓LED设备驱动:不写一行代码

文章目录一、前言二、准备工作2.1 内核版本2.2 内核文档&#xff1a;bindings->leds2.3 文档解析&#xff1a; leds-gpio.txt三、编写DTS3.1 查原理图&#xff0c;挑选GPIO3.2 编写DTS文件四、编译测试4.1 编译dt.img4.2 烧录dt.img五、基于fs的测试5.1 测试命令5.2 **点灯效…

将单向链表按照目标值value 划分成左边小,中间等,右边大的形式,给定一个单链表,判断单链表的值是否是回文结构【图文解释包你看懂】

将单向链表按照目标值value 划分成左边小&#xff0c;中间等&#xff0c;右边大的形式 例如 1 -> 3 -> 5-> 3 -> 7 按照value 3划分 1-> 3-> 3 -> 5 -> 7 解题思路&#xff1a;给定值为 value 用6个变量&#xff0c;分别表示 小于value 的Head sH &…

2023年天津中德应用技术大学专升本专业课考试具体安排及准考证

2023年天津中德应用技术大学高职升本科专业课考试准考证下载及考生须知 一、准考证下载打印 12月24日12点开始&#xff0c;专业课报名审核通过的考生&#xff0c;登录学校专业课报名系统&#xff08;114.115.135.236/jobgroup/f&#xff09;&#xff0c;点击“准考证”&#xf…

Qt之悬浮球菜单

一、概述 最近想做一个炫酷的悬浮式菜单&#xff0c;考虑到菜单展开和美观&#xff0c;所以考虑学习下Qt的动画系统和状态机内容&#xff0c;打开QtCreator的示例教程浏览了下&#xff0c;大致发现教程中2D Painting程序和Animated Tiles程序有所帮助&#xff0c;如下图所示&a…