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

news2024/12/31 5:10:40

目录

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.coloradmin.cn/o/1434410.html

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

相关文章

SpringSecurity(18)——OAuth2授权码管理

AuthorizationCodeServices public interface AuthorizationCodeServices {//为指定的身份验证创建授权代码。String createAuthorizationCode(OAuth2Authentication authentication);//使用授权码。OAuth2Authentication consumeAuthorizationCode(String code)throws Invali…

探索设计模式的魅力:外观模式简化术-隐藏复杂性,提供简洁接口的设计秘密

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 引言&#xff1a;探索简化之路 一、起源和演变 二、场景案例分析 2.1 不用模式实现&#xff1a;用一坨坨代码实现 2.2 问题 2.3 外观模式重构代码 定义 界面 接口 利用外观模式解决问题步骤 外观模式结构和说明 重构…

C语言——Q/编译和链接

目录 一、翻译环境和运⾏环境 二、翻译环境 1、预处理&#xff08;预编译&#xff09; 2、编译 2.2.1 词法分析&#xff1a; 2.2.2 语法分析 2.2.3 语义分析 3、汇编 4、链接 三、运行环境 一、翻译环境和运行环境 在ANSI C 的任何⼀种实现中&#xff0c;存在两个不…

哪种安全数据交换系统,可以满足信创环境要求?

安全数据交换系统是一种专门设计用于在不同网络环境之间安全传输数据的技术解决方案。这类系统确保数据在传输过程中的完整性、机密性和可用性&#xff0c;同时遵守相关的数据保护法规和行业标准。 使用安全数据交换系统的原因主要包括以下几点&#xff1a; 1、数据保护&#…

PHP安装后错误处理

一&#xff1a;问题 安装PHP后提示错误如下 二&#xff1a;解决 1&#xff1a;Warning: Module mysqli already loaded in Unknown on line 0解决 原因&#xff1a;通过php.ini配置文件开启mysqli扩展的时候&#xff0c;开启了多次 解决&#xff1a;将php.ini配置文件中多个…

Vue3开发环境搭建和工程结构(一)

一、NVM和Node.js安装 NVM 是 Node Version Manager&#xff08;Node 版本管理工具&#xff09;的缩写&#xff0c;是一个命令行工具&#xff0c;用于管理和切换到不同版本的 Node.js。 1、前往 nvm-windows 仓库&#xff0c;然后单击立即下载 2、下载最新版本 3 、按照安装向…

MMLAB的实例分割算法mmsegmentation

当谈及实例分割时&#xff0c;人们往往只会提到一些早期的经典算法&#xff0c;比如 PSP-Net、DeepLabv3、DeepLabv3 和 U-Net。然而&#xff0c;实例分割领域已经在过去的五六年中蓬勃发展&#xff0c;涌现出许多新的算法。今天&#xff0c;让我们一起探索这个算法库&#xff…

基于单片机的智能寻光小车设计

摘 要&#xff1a;随着物联网技术的飞速发展和逐渐成熟&#xff0c;以单片机为主的智能小车在巡查、仓储、探险及国防等领域得到广泛应用。本文设计了一种基于单片机的智能寻光小车&#xff0c;该小车以STC89C52RC 芯片为设计核心&#xff0c;结合光敏传感器和超声波传感器等多…

jmeter-问题一:关于线程组,线程数,用户数详解

文章目录 jmeter参数介绍1.线程数2.准备时长(Ramp-up)3.循环次数4.same user on each iteratio5.调度器 场景一&#xff1a;当你的线程组中线程数为1,循环为1场景二&#xff1a;当你的线程组中线程数为2&#xff0c;循环为1场景三&#xff1a;当你的线程组中线程数为1&#xff…

springboot158基于springboot的医院资源管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

ResizeObserver的使用

这篇说下ResizeObserver API。ResizeObserver接口监视 Element 内容盒或边框盒或者 SVGElement 边界尺寸的变化。 ResizeObserver避免了通过回调函数调整大小时&#xff0c;通常创建的无限回调循环和循环依赖项。它只能通过在后续的帧中处理 DOM 中更深层次的元素来做到这一点…

sqli-labs-master靶场训练笔记(21-38|精英级)

2024.1.30 level-21 (cookie 注入数据加密) 从页面上就可以看出这次的数据被 baes64 加密了 中国有句古话&#xff1a;师夷长技以制夷 &#xff0c;用base64加密后的数据即可爆出数据 加密前&#xff1a; admin and updatexml(1,concat(~,(select database()),~),1) and …

【QT】Graphics View绘图架构

目录 1 场景、视图与图形项 1.1 场景 2.2 视图 3.3 图形项 2 GraphicsView的坐标系统 2.1 图形项坐标 2.2 视图坐标 2.3 场景坐标 2.4 坐标映射 3 GraphicsView相关的类 3.1 QGraphicsView类的主要接口函数 3.2 QGraphicsScene类的主要接口函数 3.3 图形项 4 GraphicsView程序基…

谷粒商城【成神路】-【5】——品牌管理【上】

目录 &#x1f9c8;1.新建品牌分类 &#x1f95e;2.导入前端代码 &#x1f9c2;3.页面优化 &#x1f953;4.阿里云开通oss &#x1f32d;5.文件上传 &#x1f95a;6.表单后端校验 JSR303 1.新建品牌分类 在renren-fast客户端中&#xff0c;创建二级菜单&#xff0c;…

基于CNN+LSTM深度学习网络的时间序列预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 卷积神经网络&#xff08;CNN&#xff09; 4.2 长短时记忆网络&#xff08;LSTM&#xff09; 4.3 CNNLSTM网络结构 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MA…

微信自动预约小程序开发指南:从小白到专家

在数字化时代&#xff0c;预约小程序已成为各类服务行业的必备工具。本文将指导你从零开始&#xff0c;通过第三方小程序制作平台&#xff0c;顺利开发出一款具有预约功能的实用小程序。 第一步&#xff1a;注册登录第三方小程序制作平台 首先&#xff0c;你需要选择一个适合你…

麻雀搜索算法|Sparrow Search Algorithm(SSA)

在麻雀群体智慧、觅食和反捕食行为的启发下&#xff0c;提出了一种新的群体优化方法&#xff0c;即麻雀搜索算法&#xff08;SSA&#xff09;。 1、简介 在麻雀搜索算法中包含三种类型的麻雀个体&#xff0c;即发现者、跟随者和侦察者&#xff0c;三种类型对应三种行为。发现…

Intellij IDEA各种调试+开发中常见bug

Intellij IDEA中使用好Debug&#xff0c;主要包括如下内容&#xff1a; 一、Debug开篇 ①、以Debug模式启动服务&#xff0c;左边的一个按钮则是以Run模式启动。在开发中&#xff0c;我一般会直接启动Debug模式&#xff0c;方便随时调试代码。 ②、断点&#xff1a;在左边行…

下载已编译的 OpenCV 包在 Visual Studio 下实现快速配置

自己编译 OpenCV 挺麻烦的&#xff0c;配置需要耗费很长时间&#xff0c;编译也需要很长时间&#xff0c;而且无法保证能全部编译通过。利用 OpenCV 官网提供的已编译的 OpenCV 库可以节省很多时间。下面介绍安装配置方法。 1. OpenCV 官网 地址是&#xff1a;https://opencv…

用GOGS搭建GIT服务器

GOGS官网 Gogs: A painless self-hosted Git service 进入文件所在目录 cd /usr/local/develop 解压文件 tar -xvf gogs_0.13.0_linux_amd64.tar.gz 解压之后 进入gogs 目录 cd gogs 创建几个目录 userdata 存放用户数据 log文件存放进程日志 repositories 仓库根目…