memcpy函数和memove函数的实现

一、memcpy函数

1、认识memcpy函数

  看到memcpy时,你会不会有一种似曾相识的感觉。我们在学习c语言的过程中,学过一个strcpy的字符串拷贝函数,字符串拷贝,是用来拷贝字符串的。那当我们想拷贝整型数据、或者说其他类型的数据呢?这就用到我们的memcpy函数了。

参数介绍:

  我只是说想拷贝整型数据可以用memcpy,但并不是说它像strcpy函数只能拷贝字符串一样的单一。我们从memcpy函数的dest和source参数类型是void*时就可以知道,能拷贝的不止整型,所有的类型它都可以拷贝,大大提高了适用范围。

  参数介绍:destination:指向要在其中复制内容的目标数组的指针,类型转换为 void* 类型的指针。简单来说,就是你想要拷贝到哪里。

                    source:指向要在其中复制内容的目标数组的指针,类型转换为 void* 类型的指针。简单来说,就是你想要拷贝什么。

                    num:要复制的字节数。

该函数的应用:使用该函数时,我们需要加上相应的头文件#include<string.h>。其实这个函数用起来与strcpy函数类似,无非是加了一个想要拷贝的字节数。

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

}

如上述代码:把我们想拷贝的字节数,比如将arr2中40(十个整型数据大小为40个字节)个字节的数据拷贝到arr1中,我们打印一下arr1会得到以下结果:

2、实现memcpy函数

实现这个函数,我们暂且命为my_memcpy

void* my_memcpy(void* dest, const void* src, size_t sz)

参数解释:

因为我们用之前,函数并不清楚我们想要copy过去的是什么类型的数据,所以我们用void*来作为目标数据和起始数据的数据类型,在之后方便我们转化为其它类型

起始数据src前被const变量修饰是因为,我们要改的是目标数据dest,并不希望src被改变,所以我们用const修饰,以防src被改变。

我们为什么要选择一个一个字节传的方式,举个例子:当我们遇到一个整型数组,大家都知道一个整型的大小是4个字节,如果一个整型一个整型传,但当我们想传过去7个字节时,就并不会达到我们想要的结果。

代码实现:

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t sz)
{
    assert(dest&&src);
	while (sz--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return 0;
}
int main()
{
	int arr1[10] = { 0 };
	int arr2[5] = { 1,2,3,4,5 };
	int sz = sizeof(arr2) / sizeof(arr2[0]);
	my_memcpy(arr1, arr2, sz*sizeof(int));
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

这个函数的实现也是很简单了,但我们在此对函数内部操作说明一下。

1、我们第三个形式参数sz,是一个一个字节传的,那我们怎样实现呢?就将我们的dest与src强制转化为就好了,无论我们想要传的数据是什么类型, 将dest与src强制转化为char类型总能实现。

2、并且,因为我们在交换dest与src的数据的时候,对其进行了解引用。所以我们的src与dest并不能为空指针,这里用assert来判断。

打印结果如下:

3、为什么会有memmove函数?

在我们上述调用memcpy函数时,我们是将B数组中的元素拷贝到A数组中。那我如果只用一个数组呢?例如:我想将A数组中下标为0、1、2的元素拷贝到下标为1、2、3的位置。

int A[7] = { 1,2,3,4,5,6,7 };

my_memcpy(A+1, A, 3*sizeof(int));

我们仍然用我们自己实现的memcpy函数,当我们将A数组中下标为0、1、2的元素拷贝到下标为1、2、3的位置时,结果是怎样的呢?

然而,这与我们想要的结果不同,我们想要的结果是1、1、2、3、5、6、7。

出现与我们结果不同的原因是,在我们把第一个元素的值拷贝给第二个元素时,覆盖了原来的元素的值。第二个元素的值改变,所以当我们再将第二个元素的值拷贝给第三个元素时,又会将第三个元素的值覆盖掉。所以才有了上述的错误结果。

标准值规定,memcpy来实现不重叠的内存的拷贝。为了解决重叠内存的拷贝,便有了memmove函数。

二、memmove函数

1、认识memmove函数

参数介绍:

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

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

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

形参的类型与memcpy一致,作用也相同。

调用memmove函数:

我们上述在解决重叠内存的拷贝时,用memcpy是行不通的,我们在这里调用一下memmove函数,让大家清楚他的用处和使用。

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

结果:

结果显然是我们想要的,那么memmove是怎样实现的呢?

2、模拟实现memmove函数

图解:

F:倒序拷贝:当我们的数据源的起始地址在目标数组的起始地址的前面时,用倒叙拷贝。

     倒序,也就是从后往前排序,例如我们上述例子,其实就是一个倒叙拷贝,先拷贝A[2]到A[3]的位置,再拷贝A[1]到A[2]的位置,最后再拷贝A[0]到A[1]的位置,这时候我们不用担心覆盖,因为我们是先将A[2]的值拷贝过去后,已经完成了它的拷贝任务,就不用再担心别人会来阻碍它完成任务了。

S:正序拷贝:也就是从前往后按顺序拷贝。当我们的目标数组dest的起始地址在数据源source的起始地址的前面时,用正叙拷贝。

这时候我们先把A[1]拷贝到A[0]中,先完成A[1]的拷贝任务,接着再让A[2]拷贝到A[1]位置上,因为A[1]的拷贝任务已经完成了,所以我们即使覆盖它也没事,后面的步骤也是如此。

代码实现:

        

#include<stdio.h>
#include<assert.h>
void* my_memove(void* dest, void* source, size_t sz)
{
	assert(dest&&source)
    void*ret=dest;
	if (dest<source)
	{
		for (int i = 0; i < sz; i++)
		{
			*(char*)dest = *(char*)source;
			dest = (char*)dest + 1;
			source = (char*)source + 1;
		}
	}
	else(dest > source)
	{
		for (int i = sz; i >0; i++)
		{
			*((char*)dest + sz) = *((char*)source + sz);
		}
	}
    return ret;
}
int main()
{
	int arr[7] = { 1,2,3,4,5,6,7 };
	my_memmove(arr + 1, arr, 3 * sizeof(int));
	for (i = 0; i < 7; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

图解思想讲完,写出代码也是易如反掌,重要的还是思想。

但是写代码时要注意,虽然source和dest是一个数组中的,但是他们的起始地址不同。

另外,平常写代码要注意严谨规范,比如assert判断是否为空指针 注意void*类型的函数的返回值

让我们的代码更加完美

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值