实现memcpy函数和memmove函数
前言
本文介绍了如何自己实现memcpy函数和memmove函数的一些思路。
memcpy库函数
- 以下是memcpy函数的文档解释
void *memcpy(void str1, const void str2, size_t n)
参数
str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void 指针。
str2 – 指向要复制的数据源,类型强制转换为 void 指针。
n – 要被复制的字节数。
返回值
该函数返回一个指向目标存储区 str1 的指针。
笔者上一篇写了如何实现strcpy函数,这个函数和strcpy函数十分相识,而strcpy函数只能拷贝字符串,memcpy可以拷贝任意类型。
它传入和返回的指针都是void类型,void类型的指针是不允许进行解引用操作和指针加减运算的。strcpy函数只能拷贝字符串,但是memcpy可以拷贝任意类型。
传入的指针都是void*类型的,我们需要把它们转换成其他类型的指针才能进行操作,这里强转成char*类型,char*类型一次操作一个字节,更好进行条件判断。
代码如下(示例):
void* my_memmove(void* dest, const void* src, size_t sum)
{
assert(dest && src);
void* res = dest;//记录dest初始位置。
while (sum--) //sum大于0就进行拷贝。
{
*(char*)dest = *(char*)src; //一次拷贝一个字节数据。
++(char*)dest;//每次填过一个字节的空间。
++(char*)src;
}
return res;//返回初始指针。
}
memmove函数
memmove函数和memcpy函数的定义一样。
C语言标准规定memcpy函数只需实现拷贝功能,无需考虑内存重叠问题。
memmove函数则必须解决内存重叠问题,对数据进行正确拷贝,可以看做是memcpy函数的升级版。
但是在vs2013或者vs2019环境下memcpy也实现了memmove功能。
内存重叠是什么意思呢?
如图,假设我们使用memcpy函数把dest指针指向的内容拷贝到src指向的空间,一共拷贝5个int数,共20个字节。
int arr1[] = {
1,2,3,4,5,6,7,8,9 };
int arr2[10] = {
0 };
my_memcpy(arr1 + 4, arr1 + 2, 20); //在vs编译器以下,memcpy也实现了
int i;
for (i = 0;i < 9;i++)
{
printf("%d ", arr1[i]);
}
此时调用上面写的memcpy函数,打印dest,输出:1 2 3 4 3 4 3 4 3,此时arr1数组为下图:
当使用memcpy函数对有内存重叠程序进行拷贝出问题了。
为什么呢,当src给dest进行拷贝,3赋值给了5,4赋值给了6,数组原来5,6的位置的数据已经被改成了3,4。
有什么办法解决这个问题呢。之前都是从前向后拷贝,这时候尝试一下从后向前拷。
从后向前拷贝解决了内存重叠尝试的问题。但是此时的dest指针是大于src指针的,如果dest指针小于src指针又是什么样。
此时dest在src右边。从前往后拷贝,不会出现问题。
从后往前拷贝,此时又出现了内存重叠问题。
我们可以得出结论。如果dest小于src,就从做前向后拷贝。dest大于src,就从后向前拷贝。如果不存在内存重叠问题,那么使用哪种方式都可以。
代码从后向前拷贝如何实现。
当前平台vs2019是小端存储模式。
假设,如图进行拷贝。src + count - 1,dest+ count - 1我们就得到了从后向前拷贝的第一个字节。
代码实现(示例):
void* my_memmove(void* dest, const void* src, size_t sum)
{
assert(dest && src);
void* res = dest;
if (dest < src)///如果dest小于src,从前向后拷贝。
{
while (sum--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
}
else //大于或不重叠从后先前拷。
{
while (sum--) //第一次sum =19 ,当sum = 0 停止拷贝
{
*((char*)dest + sum) = *((char*)src + sum);
}
}
return res;
}