memcpy与memmove的目的都是将N个字节的源内存地址的内容拷贝到目标内存地址中。
我们先实现memcpy的功能:
#include <stdio.h>
#include <assert.h>
void *my_memcpy(void *dst,const void *src,int n)
{
void *ret = dst;
char *p1 = (char*)dst;
char *p2 = (char*)src;
assert(dst);
assert(src);
while(n--)
{// 注意,memcpy函数没有处理dst和src区域是否重叠的问题
*p1++ = *p2++;
}
*p1 = '\0';
return ret;
}
int main ()
{
char p[] = "bit-tech";
char q[10];
my_memcpy(q,p,4);
printf("%s\n",q);
return 0;
}
函数原型中的形参设置为void *型是为了使用户每次传入不同类型的实参时,函数都能接收。但如果需要对形参进行运算,那么必须对形参进行强制类型转换。
那么我们接着来看一下memmove函数的实现:
memmove的处理措施:
(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝
(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝
(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝
#include <stdio.h>
#include <assert.h>
void *my_memmove(void *dst,const void *src,int n)
{
void *ret = dst;
char *p1 = (char*)dst;
char *p2 = (char*)src;
assert(dst);
assert(src);
if((p2 <= p1) && (p1 <= p2+n))
{ // 若dst和src区域交叉,则从尾部开始向起始位置拷贝,这样可以避免数据冲突
p2 = p2+n-1;
p1 = p1+n-1;
while(n--)
{
*p1 = *p2;
p1--;
p2--;
}
}
else
{ // 若dst和src区域没有重叠,则从起始处开始逐一拷贝
while(n--)
{
*p1 = *p2;
p1++;
p2++;
}
*p1 = '\0';
}
return ret;
}
int main ()
{
char p[] = "bit-tech";
char q[10];
my_memmove(p+1,p,6);
printf("%s\n",p);
my_memmove(q,p,6);
printf("%s\n",q);
return 0;
}
总之,两者的区别在于:
当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。