内存函数memcpy、memmove原理及模拟实现

        hello大家好,我是阿乐。进来我们主要说说两个内存函数memcpy、memmove的原理以及模拟实现,希望各位看官老爷们能有所收获有所帮助!同样我也在不断学习和改进当中,有什么问题欢迎大家评论区留言或者私信哦。

        话不多说,让我们直接进入正题!

目录

一、memcpy函数

1.memcpy函数介绍及实现

 2.memcpy函数的模拟实现

 二、memmove函数

1.memmove函数介绍及实现

   2.memmove函数的模拟实现


一、memcpy函数

1.memcpy函数介绍及实现

(1)函数原型

void *memcpy(void*dest, const void *src, size_t n);

(2)功能

由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。

(3)头文件

#include<string.h>

(4)返回值

  函数返回一个指向dest的指针。

(5)说明

        1.source和destin所指内存区域不能重叠,函数返回指向destin的指针。

        2.与strcpy相比,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。

        memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;

例:

  char x[100], y[50];

  memcpy(y, x,sizeof(y)); //注意如用sizeof(x),会造成y的内存地址溢出。

  而strcpy就只能拷贝字符串了,它遇到'\0'就结束拷贝;

例:

  char a[100], b[50];

       strcpy(a,b);

        3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。

  注意:source和destin都不一定是数组,任意的可读写的空间均可。

例子:

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	//将arr1中的内容,拷贝到arr2中
	memcpy(arr2, arr1, 40);

	int i = 0;
	for (i = 0; i < 20; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

代码调试结果如下: 

 2.memcpy函数的模拟实现

我们直接上代码,再来一步一步解读。

void* my_memcpy(void* det, const void* src, int num)
{
	void* ret = det;
	while (num)
	{
		*(char*)det = *(char*)src;
		det = (char*)det + 1;
		src = (char*)src + 1;
		num--;
	}
	return ret;
}
int main()
{

	float arr1[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
	float arr2[10];
	int i = 0;
	int len = sizeof(arr1);
	my_memcpy(arr2, arr1, len);
	for (i = 0; i < 9; i++)
	{
		printf("%0.8lf ", arr2[i]);
	}
	return 0;
}

(1)首先,是给我们模拟的函数起个名为my_memcpy。这样方便我们理解的同时也不失为一个好听的名字~

(2)然后就是根据memcpy函数的参数来给我们的my_memcpy函数对应起来,模仿的函数原型如下

void* my_memcpy(void* det, const void* src, int num)

 (3)接下来就是具体实现我们的my_memcpy函数了。

一般我们用地址赋值都会先把地址解引用并赋值,然后让地址++。

*dest = *src;
dest++;
src++;

可是,在模拟函数中,传参的地址都是void*类型的,当我们让地址++,地址不知道要跳几个字节。

比如:char* 类型的指针++,向后跳1个字节。而int* 类型的指针++,向后跳4个字节。

现在我们面临两个问题:

        1.指针类型为void*,无法++。

        2.要拷贝内容的个数不知道,通俗点就是我们不知道要拷贝几次或者两个指针要++几次。

 对于第一个问题,我们把指针强制类型转换,这样就可以++了。但是如果让指针类型转换成int*话,我们就要循环count/sizeof(int)次,这样太麻烦我们还带确定用户要拷贝内容的类型。

对于这个问题我们可以把指针类型转换成char* ,因为char*类型指针++向后跳一个字节(一个字节一个字节访问),不管是什么类型我们都可以一个一个字节的拷贝,这样我们也容易确定循环次数,为count。

注意:我们也不要忘了,要拷贝一下目标地址的起始位置,因为函数要返回目标空间起始地址


 二、memmove函数

1.memmove函数介绍及实现

(1)函数原型:

        void *memmove(void *str1, const void *str2, size_t n)

(2)功能:

        从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。

(3)头文件:

        #include <string.h>

(4)返回值:

        该函数返回一个指向目标存储区 str1 的指针。

(5)说明:

         memcpy()函数如果遇到内存重叠,就无法得到理想的拷贝。而memmove()函数就恰恰解决了这一问题。

例子:

#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.memmove函数的模拟实现

还是和上面一样,先上代码。

void* my_memmove(void* dest, const void* src, int n)
{
	char* pdest = (char*)dest;
	const char* psrc = (const char*)src;
	assert(dest);
	assert(src);
	if (pdest <= psrc && pdest >= psrc + n)//正常情况下从前向后拷贝
	{
		while (n--)
		{
			*pdest = *psrc;
		}
	}
	else //当出现内存覆盖时从后向前拷贝
	{
		while (n--)
		{
			*(pdest + n) = *(psrc + n);
		}
	}
	return dest;
}

int main()
{
	char arr[10] = "abcdefg";
	char arr0[10] = "abcdefg";
	char arr1[10] = { 0 };
	my_memmove(arr + 2, arr, 4);
	my_memmove(arr1, arr0, 4);
	printf("%s\n", arr);
	printf("%s\n", arr0);
	printf("%s\n", arr1);
	return 0;
}

首先 memmove 是一个内存操作函数,不是字符串操作函数,它可以处理多种类型的数据。

它的原型是:void *memmove( void* dest, const void* src, size_t count );

这里有两个点需要记住:

1、它的返回值是void*,参数类型也是void*,这样它才可以处理不同类型的数据。

2、目标dest不能加const,源src加const。原因是我们需要从源src中拷贝数据到dest中,需要对dest进行赋值。若用const保护dest,便不能完成赋值操作。

memmove的好处是可以处理dest与src发生内存重叠的情况


        今天的内容到这里就结束啦,希望以上内容对你们能有所帮助!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值