目录

前言:

memcpy:

介绍:​编辑

模拟实现memcpy:

memmove:

介绍:

模拟实现memmove:

总结:


前言:

在上一篇blog中,我们探讨了有关strcpy和strcmp等的字符串函数和对其的模拟实现。

今天,我们着重讨论有关memcpy和memmove这两种内存函数,并模拟实现它们。

首先我们要知道内存函数和字符串函数的区别,

字符串函数像strcpy和ctrcmp的使用对象都为字符串,而面对整形或者浮点型就不适用了,因此我们在这里引出了内存函数。

内存函数像memcpy和memmove等的使用对象是内存中的各个数据,不仅仅可以对于字符串还可以对整形数组或者字符数组进行操作。接下来我们来介绍这两个函数。

memcpy:

介绍:

对于memcpy它的代码如下:

void* memcpy(void* destination, const void* source, size_t num)

destination----为需要将源头拷贝到的区域。

 source——为源头将其拷贝到destination。

num——为单位,表示要拷贝多少个字节到destination。

具体操作如下:

#define _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>
#include<string.h>
int main()
{
	int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	int source[] = { 9, 10 };
	memcpy(destination, source, 8);
	
	int sz = sizeof(destination) / sizeof(destination[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", destination[i]);
	}
	return 0;
}

我们预期输出9, 10, 3, 4, 5, 6, 7, 8

实际输出结果与预期结果一致。

接下来我们来谈谈怎么模拟实现memcpy

模拟实现memcpy:

在刚刚的介绍中,我们知道memcpy的表达式是

void*memcpy(void* destination, const void* source, size_t num)

我们需要知道他们各个参赛的返回类型,基本都为void*,那么实现他们就和实现qsort如出一辙。

代码如下:
 

#define _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	while (num--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;
		//注意不可进行(char*)destionation++,因为强制类型转换是临时的,强制类型转换完使用后就没了,这样在++就是对void*加加了,
		//而void*是不可以进行++操作的。
	}
	return ret;
}

int main()
{
	int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	int source[] = { 9, 10 };
	//memcpy(destination, source, 8);
	my_memcpy(destination, source, 8);

	int sz = sizeof(destination) / sizeof(destination[0]);

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

以下代码的操作方式与之前我们实现qsort函数的算法相似,具体可以参考之前的qsort的blog。

        *(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;

自主实现qsort函数_无双@的博客-CSDN博客

memmove:

介绍:

我们会发现,该函数的各个参数返回类型与memcpy大为相似,其实是的,我们可以将memmove理解为更为完善的memcpy,我们现阶段不需要对他们进行更深的理解。

什么时候使用memmove呢?

我们先讲讲my_memcpy的不足之处:

如果我们需要将destination中的1,2,3,4,5拷贝覆盖到3,4,5,6,7

预期结果应当为:

1,2,3,4,5,8

可如果我们使用my_memcpy会出现一下结果:

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	while (num--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;
		//注意不可进行(char*)destionation++,因为强制类型转换是临时的,强制类型转换完使用后就没了,这样在++就是对void*加加了,
		//而void*是不可以进行++操作的。
	}
	return ret;
}

int main()
{
	int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	//int source[] = { 9, 10 };
	//memcpy(destination, source, 8);
	my_memcpy(destination + 2, destination, 20);

	int sz = sizeof(destination) / sizeof(destination[0]);

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

最后的输出结果与我们的预期结果不一样。

图示如此,接下来实现操作。

当我们拷贝完1,2时,我们即将要拷贝3时,会发现3我们找不到了,3的位置变成了1,4的位置也变成了2.

所以才会出现重复的1,2,1,2.

因此面对这种情况,我们可以采取用memmove实现,或者进行逆序排序。如图:

	memmove(destination + 2, destination, 20);

再输出结果就会得到与预期一致的数据:

这就是memmove与memcpy的区别。

但如果你尝试使用memcpy来进行拷贝,会发现也与memmove一样出现预期结果。

那是因为VS编译器对memcpy进行了优化。

接下来我们来讲讲模拟实现memmove


模拟实现memmove:

具体代码如下:

#include<assert.h>
void* my_memmove(char* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	if (destination < source)
	{
		//前往后拷贝
		for (int i = 0; i < num; i++)
		{
			*(char*)destination = *(char*)source;
			destination = (char*)destination + 1;
			source = (char*)source + 1;
		}
	}
	else
	{
		//从后向前拷贝
		while (num--)
		{
			*((char*)destination + num) = *((char*)source + num);
		}
	}
	
	return ret;
}

int main()
{
	int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	//int source[] = { 9, 10 };
	//memcpy(destination, source, 8);
	memmove(destination + 2, destination, 20);

	int sz = sizeof(destination) / sizeof(destination[0]);

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

我们来着重分析下伪代码:

void* my_memmove(char* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	if (destination < source)
	{
		//前往后拷贝
		for (int i = 0; i < num; i++)
		{
			*(char*)destination = *(char*)source;
			destination = (char*)destination + 1;
			source = (char*)source + 1;
		}
	}
	else
	{
		//从后向前拷贝
		while (num--)
		{
			*((char*)destination + num) = *((char*)source + num);
		}
	}
	
	return ret;
}

这一部分是判断destination与source的关系,如果我们此时我的destination小于source,那我们就不能从往前拷贝,而是应当选择从前往后,不然出错原因与上述所讲的一致,会被占用掉。

实现拷贝也是与qsort函数哪一部分类似,在这里我也不进行多的赘述。

具体的方法就是将destination和source从void*转换成char*。

 

接下来就是当destination大于source时,选择的从后往前进行拷贝。

 目前是如图所示:

当我想要访问source和destination指向的最后一个元素时,可以选择加上sum

具体解释如图:

如果我们加上4时,即跳过一个整形,此时指针就会指向:

 就会指向下一个元素的首字节。

因此我们需要+num再-1,但我们在while语句进行判断的时候,已经实现了num后置--,所以则不需要再进行-1操作。

如此运行结果为:

以上就是my_memove的实现过程。

总结:

 在本文我们主要讲解了memcpy和memmove的内存函数,并模拟实现了这两种函数。

我们在此学会了可以下来尝试动手自己编写编写。

这样我们对此会愈发熟悉。

记住

“坐而言不如起而行”

Action speak louder than words!

若先要看源码可以访问我的Gitee:

Test_memcpy_memmove_CSDN/Test_memcpy_memmove_CSDN/test.c · 无双/test_c_with_X1 - Gitee.com

Logo

一站式 AI 云服务平台

更多推荐