前言
memcpy与memmove函数是C语言中提供的内存库函数,下列文章主要介绍这两个函数,以及如何自己实现
一、memcpy与memmove函数的介绍
首先我们先了解memcpy函数
memcpy有三个形参,分别是
- void * dest:目标数组的首地址
- const void * src:源头数组的首地址(const可以使程序更加安全,不容易出错)
- size_t: count:需要拷贝的字节数
- 返回类型:void* 返回的是dest数组的首地址
其函数的作用是将src数组内的count个字节的数据拷贝到dest数组中
memmove函数
memmove函数同样有三个形参
- void * dest:目标数组的首地址
- const void * src:源头数组的首地址(const可以使程序更加安全,不容易出错)
- size_t: count:需要移动的字节数
- 返回类型:void* 返回的是dest数组的首地址
其函数的作用是将src数组内的count个字节的数据拷贝到dest数组中
这里有的小伙伴可能会好奇,为什么memmove和memcpy两个函数的作用相同
其实是因为memmove考虑的情况会更多
memmove函数和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的,如果源空间和目标空间出现重叠,就得使用memmove函数处理
例如在一个int类型的数组中进行有重叠的操作时 memcpy就不能很好的完成对应的任务
注意:两个地址形参定义成void的指针是因为函数的设计者并不知道使用者需要拷贝的是什么类型是数据,所以通过void类型的指针接受,而在后面的实现函数时我们需要将其强制类型转换成char*类型的指针进行操作
二、memcpy与memmove函数自实现
就像上文所提到的 memcpy和memmove地址形参是void类型的,由于void类型的指针不能直接解引用得到地址背后的值,所以我们在拷贝的时候需要进行类型转换,而转化成什么类型的指针很重要,在这里我们通常转换成char*类型的指针,因为这样可以很好的适配其他类型,可以理解为单位1,就像一个标准,例如RMB中的1元。
函数中的第三个形参 size_t count 也有异曲同工之妙,用一个字节作为一个最小单位表达需要拷贝的数据长度
了解完这些memcpy基本的思路 就已经出来了 因为memcpy不考虑数据重叠的情况
这里直接放代码
void* my_memcpy(void* dest, const void* src, size_t count)
{
void* ret = (char*)dest;//这里先将dest的地址存起来,以便返回
while (count)//利用count控制需要做的循环次数,以实现目标
{
*(char*)dest = *(char*)src;
dest = ((char*)dest) + 1;//每次拷贝后dest和src都往后走1个字节
src = (char*)src + 1;//每次拷贝后dest和src都往后走1个字节
count--;
}
return ret;
}
当数据不重叠的时候 我们不管是从左到右还是 从右到左都能进行交换也就是memcpy可以适配的情况 而memmove函数就需要考虑数据可能会重叠的情况,当数据重叠时,则会有待用数据被更改的情况,我们需要分类讨论,找出影响结果的方式
我们以下图为例进行分析
得出以上结果我们可以归为三类:
一类是不存在重叠的部分,不管从哪边开始都行
一类是重叠部分目标数据在目的地的左边,需要数据从后向前更改
一类是重叠部分目标数据在目的地的右边,需要数据从前向后更改
则我们可以得出以下代码:
void* my_memmove(void* dest, const void* src, size_t count)
{
void* ret = dest;
if (dest < src)
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
if (dest > src)
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return ret;
}
从以上我们可以得出memmove函数的功能比memcpy的功能更加强大