模拟实现memcpy和memmove
在上一篇blog中,我们探讨了有关strcpy和strcmp等的字符串函数和对其的模拟实现。今天,我们着重讨论有关memcpy和memmove这两种内存函数,并模拟实现它们。首先我们要知道内存函数和字符串函数的区别,字符串函数像strcpy和ctrcmp的使用对象都为字符串,而面对整形或者浮点型就不适用了,因此我们在这里引出了内存函数。内存函数像memcpy和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;
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
更多推荐




所有评论(0)