C语言内存函数:memcpy、memcat、memmove介绍和模拟实现(实用性高,建议三连收藏)

目录

1.memcpy函数 

1.1函数介绍

1.2函数示范使用 

1.3函数的模拟实现

 1.4补充

2.memmove函数

2.1函数介绍

2.2函数的使用示范 

2.3函数的模拟实现 

3.memcmp(内存比较函数) 

3.1函数介绍

 3.2函数的示范使用,有趣的例子

4.函数补充memset(内存设置函数) 

4.1函数介绍

4.2函数示范使用 

5.结语


 

1.memcpy函数 

引入:之前我们讲过字符串的拷贝函数,但是当我们要拷贝整型数据或者结构体数组等类型弟弟数据的时候,就需要一个包容性更好的函数了。 

1.1函数介绍

函数头文件:string.h

函数参数:

①目的地

指向要复制内容的目标数组的指针,类型转换为 void* 类型的指针。

②源

指向要复制的数据源的指针,类型转换为 const void* 类型的指针。

③数量

要复制的字节数。 size_t 是无符号整数类型。

返回值类型:void*

返回值:返回目的字符串的地址也就是考到到某个地方,这个某个地方的地址。

函数功能:

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。


1.2函数示范使用 

struct {char name[40];int age;
} person, person_copy;
int main()
{char myname[] = "Pierre de Fermat";memcpy(person.name, myname, strlen(myname) + 1);person.age = 46;memcpy(&person_copy, &person, sizeof(person));printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);return 0;
}

输出: 

1.3函数的模拟实现

函数模拟实现思想:我们参照库函数1来写,我们的想法是将源src的地址起的num个自己的内容拷贝到以目的地址dest为起点的空间中。那么由于我们的函数是要实现不同类型的内容都可以兼容拷贝,那么参数肯定是要设计为我们的void*类型来实现广泛接受,当我们实现的时候我们又想一个字节一个字节的遍历,那就应该将原先的void*的地址,转换为char*d的地址,以num为循环遍历拷贝条件,最后返回我们的目的空间的其实地址就行。

加下来我们看代码实现:

#include<stdio.h>
#include<assert.h>
#include <string.h>void* my_memcpy(void* dst, const void* src, size_t count)
{void* ret = dst;//保存目的空间地址assert(dst);assert(src);//断言,因为后续有指针的解引用操作,防止指针为空指针while (count--){*(char*)dst = *(char*)src;dst = (char*)dst + 1;src = (char*)src + 1;}return ret;
}struct {char name[40];int age;
} person, person_copy;
int main()
{char myname[] = "woaini";my_memcpy(person.name, myname, strlen(myname) + 1);person.age = 46;my_memcpy(&person_copy, &person, sizeof(person));printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);return 0;
}

我们来看一下实现效果:

 

 1.4补充

①关于 dst = (char*)dst + 1;指针的移动

首先我们知道void* l类型的指针是不支持指针运算的,所以要强制转换类型,然后因为char*类型的指针一步的长度刚好是一个字节,控制力度较好控制,所以转换为了字符型指针。

那么

可以这样写吗:(char*)dest++;

这种写法是错误的,因为强制类型转换是临时的,强制类型并不会改变这个指针本身弟弟类型,只是我用的时候临时转换的,后置++是先试用再++,等加加的时候,已经不能够在使用了。

那么:++(char*)dest 呢

这种写法可以但是有些编译器会报错,所以还是比较推荐第一种写法。

②我们想一下这个问题:

如果我们拷贝的空间是重复的呢,,比如我们有一个整型数组:

int arr1 = [1,2,3,4,5,6]

我们这样写:memecpy(arr1+2,arr1,60

我们原本是想得到:
1,2,1,2,3,4,5,6

但是如果我们使用memcpy就会出现这样的情况

但是,当我们去编译器里实现这段代码我们来看一下结果:

 

我们发怎么和我们预计的不一样,这个函数好像对于重叠的内存空间也能正常拷贝呀

这就是最后要补充的点:

memcpy确实是不负责重叠拷贝的。这是因为最初设计memcpy函数的时候我们是要求只用实现不重叠拷贝就好(相当于60分),在Vs上函数实现了重叠拷贝(相当于100分),哪也可以,但是我们接下来要介绍的这个函数才是正宗的负责重叠拷贝的。我们接着next.

2.memmove函数

2.1函数介绍

 

函数头文件:<string.h>

函数参数:

void * destination:指向要复制内容的目标数组的指针,类型转换为 void* 类型的指针。
const void * source:指向要复制的数据源的指针,类型转换为 const void* 类型的指针。size_t num :要复制的字节数。 size_t 是无符号整数类型。

 返回值:返回目标数组的地址

函数功能:将 num 字节的值从指向的位置复制到目标指向的内存块。复制就像使用中间缓冲区一样进行,从而允许目标重叠。

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

2.2函数的使用示范 

刚刚上面举例的数组就是一个示范,下面用一个字符数组来示范:

#include <stdio.h>
#include <string.h>
int main ()
{char str[] = "memmove can be very useful......";memmove (str+20,str+15,11);puts (str);return 0;
}

2.3函数的模拟实现 

对于函数的返回值由于不知道传递进入的目标首地址会是什么类型,我们就用Void*作为返回类型,对于函数参数的设计,目的数组和源数组的地址都不确定是什么类型所以设计为void*类型,由于源字符串我们不会去操作改变,所以我们用const修饰。

由于函数实现的同一片空间的内容拷贝所以请看图解实现思想:

由上图得出结论一:当我们的dst<= src,也就是说目标地址要小于源地址的时候应该先将源字符串首地址的内容拷贝到目的数组的地址中,循环拷贝num个字节。 

结论②:当src<=dest<=src+num时, 目标地址要大于等于源地址并且小于等于源地址+num字节的的时候应该先将源字符串尾部地址的内容拷贝到目的数组的尾部地址中,循环拷贝num个字节。

当dest大于src+num时,随便怎么拷贝循序都行1,为了方便书写条件,我们就可以将其归结到上述两种情况其中之一,最好是第二种情况。 

 第一种实现:

void* my_memmove(void* dest, const void* src, size_t num)
{void* ret = dest;//保存目的空间地址assert(dest && src);//断言判断if (dest < src)//虽然void*不可以解引用或者算术运算,但是可以比较大小,因为保存的是地址编号{while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else{while (num--){*((char*)dest + num) = *((char*)src + num);}}return ret;
}
int main()
{char str[] = "memmove can be very useful......";my_memmove(str + 20, str + 15, 11);puts(str);return 0;
}

第二种实现大家可以试一下:

void * memmove ( void * dst, const void * src, size_t count)
{void * ret = dst;if (dst <= src || (char *)dst >= ((char *)src + count)){
while (count--) {*(char *)dst = *(char *)src;dst = (char *)dst + 1;src = (char *)src + 1;}}else {dst = (char *)dst + count - 1;src = (char *)src + count - 1;while (count--) {*(char *)dst = *(char *)src;dst = (char *)dst - 1;src = (char *)src - 1;}}return(ret);
}

3.memcmp(内存比较函数) 

3.1函数介绍

头文件包含:string.h

函数参数: 

PTR1型

指向内存块的指针。

PTR2型

指向内存块的指针。

数量

要比较的字节数。

返回值:

返回值表明
<0在两个内存块中不匹配的第一个字节在 ptr1 中的值低于 ptr2 中的值(如果计算为 unsigned char 值)
0两个内存块的内容相等
>0在两个内存块中不匹配的第一个字节在 ptr1 中的值大于 ptr2 中的值(如果计算为 unsigned char 值)
函数:是根据指针指向的内容一个字节一个字节的往后面进行比较,直到比出大小或者达到要求比较的字节数num.

 3.2函数的示范使用,有趣的例子

int main()
{int arr1[] = { 1,2,1,4,5,6 };int  arr2[] = { 1,2,257 };int ret1 = memcmp(arr1, arr2, 9);int ret2 = memcmp(arr1, arr2, 10);printf("%d\n%d\n", ret1, ret2);return 0;
}

两个数组前九个字节是一模一样的,这样说明我们的这个memcmp是一个字节一个字节的比较的

4.函数补充memset(内存设置函数) 

4.1函数介绍

 函数功能:将指定空间的num个字节设置为指定的value值,返回的是指定空间的地址。

4.2函数示范使用 

int main()
{char arr[] = { "hello word" };memset(arr + 1, 'x', 4);printf("%s\n", arr);return 0;
}

 

5.结语

以上就是本期的所有内容,知识含量蛮多,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,感谢大家的关注与喜欢。

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

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

相关文章

Pandas教程11:关于pd.DataFrame.shift(1)数据下移的示例用法

---------------pandas数据分析集合--------------- Python教程71&#xff1a;学习Pandas中一维数组Series Python教程74&#xff1a;Pandas中DataFrame数据创建方法及缺失值与重复值处理 Pandas数据化分析&#xff0c;DataFrame行列索引数据的选取&#xff0c;增加&#xff0c…

【RT-DETR有效改进】UNetv2提出的一种SDI多层次特征融合模块(细节高效涨点)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本问给大家带来的改进机制是UNetv2提出的一种多层次特征融合模块(SDI)其是一种用于替换Concat操作的模块,SDI模块的主要思想是通过整合编码器生成的层级特征图来增强图像中的语义信息和细节信息。包括皮肤…

数据库管理-第144期 深入使用EMCC-01(20240204)

数据库管理144期 2024-02-04 数据库管理-第144期 深入使用EMCC-01&#xff08;20240204&#xff09;1 用户管理2 配置告警动作3 配置意外事件规则总结 数据库管理-第144期 深入使用EMCC-01&#xff08;20240204&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&am…

redis(6)

文章目录 一、redis clusterRedis Cluster 工作原理Redis cluster 基本架构Redis cluster主从架构Redis Cluster 部署架构说明部署方式介绍 原生命令手动部署原生命令实战案例&#xff1a;利用原生命令手动部署redis cluster 实战案例&#xff1a;基于Redis 5 的redis cluster部…

Matplotlib绘制炫酷柱状图的艺术与技巧【第60篇—python:Matplotlib绘制柱状图】

文章目录 Matplotlib绘制炫酷柱状图的艺术与技巧1. 簇状柱状图2. 堆积柱状图3. 横向柱状图4. 百分比柱状图5. 3D柱状图6. 堆积横向柱状图7. 多系列百分比柱状图8. 3D堆积柱状图9. 带有误差线的柱状图10. 分组百分比柱状图11. 水平堆积柱状图12. 多面板柱状图13. 自定义颜色和样…

2024.2.5日总结(小程序开发2)

小程序的宿主环境 宿主环境 宿主环境指的是程序运行所必须的依赖环境。 Android系统和iOS系统是两个不同的宿主环境。安卓版的微信App不能再iOS环境下运行。Android是安卓软件的宿主环境&#xff0c;脱离了宿主环境的软件是没有意义的。 小程序的宿主环境 手机微信是小程序…

vue全家桶之状态管理Pinia

一、Pinia和Vuex的对比 1.什么是Pinia呢&#xff1f; Pinia&#xff08;发音为/piːnjʌ/&#xff0c;如英语中的“peenya”&#xff09;是最接近pia&#xff08;西班牙语中的菠萝&#xff09;的词&#xff1b; Pinia开始于大概2019年&#xff0c;最初是作为一个实验为Vue重新…

【ArcGIS微课1000例】0102:面状要素空洞填充

文章目录 一、实验描述二、实验数据三、实验步骤1. 手动补全空洞2. 批量补全空洞四、注意事项一、实验描述 在对地理数据进行编辑时,时常会遇到面数据中存在个别或大量的空洞,考虑实际情况中空洞的数量多少、分布情况,填充空洞区域可以采用逐个填充的方式,也可以采用快速大…

【Django开发】美多商城项目第3篇:用户注册和图片验证码开发(附代码,文档已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现…

构建高效直播美颜系统:美颜SDK集成与性能优化指南

如今&#xff0c;美颜技术的广泛应用成为各类直播平台的标配之一。今天&#xff0c;小编将与大家进一步讨论如何构建高效的直播美颜系统&#xff0c;重点关注美颜SDK的集成和性能优化方面。 一、美颜SDK的选择与集成 选择合适的美颜SDK是构建高效直播美颜系统的第一步。不同的…

一台机器上如何部署多个web项目

1、综述 随着计算机硬件水平的不断提高&#xff0c;往往不是一台机器上只部署一个web项目了&#xff0c;而是尽可能多部署几个项目&#xff0c;以用来节省资源&#xff0c;现在我们看看如何一台机器部署多个项目&#xff0c;我们先结合上一篇文章中提到的tomcat架构&#xff0…

美创科技与河南金融信创生态实验室签署战略合作协议

2024年1月31日&#xff0c;由普惠通科技与河南省科学院物理所、北京交通大学、中国金融电子化集团重庆金融认证中心联合发起成立中部地区第一家金融信创生态实验室运营公司&#xff08;即河南豫科普惠通信创科技有限公司&#xff09;与杭州美创科技股份有限公司战略合作签约仪式…