下手实现memcopy函数
动手实现memcopy函数
函数原型:MemCopy(void* from,void* to,int n);
函数功能:把 from 指向的内存中的n个字节复制到to指向的内存中。
分析:这个有个陷阱就是 to 和 from 两个地址谁大谁小,以及是否重叠。我们假设两段内存地址范围为: from ~ from +(n-1), to ~ to+(n-1)(长度都为 n ),为了说明下面的情形,我们举一个例: 数组 array[8]={0,1,2,3,4,5,6,7};
情形1:【举例 from = array,to = arary+4 ,n=3】
from +(n-1) < to 或者 to+(n-1) < from ,即两段内存没有任何重合,这时
从 头部 from 开始拷贝到尾部 from+(n-1),
即 *from->*to , *(from+1)->*(to+1) ... *(from+(n-1)) -> *(to+(n-1))
或者
从尾部from+(n-1) 开始拷贝到 from ,
即*(from+(n-1)) -> *(to+(n-1)) ... *(from+1)->*(to+1) ,*from->*to
都是没有问题。结果都是 :array[8]={0,1,2,3,0,1,2,7};
换句话说你可以正向拷贝,也可以反向拷贝。
情形2:【举例 from = arrary,to=array+2,n=3】
from < to <from +(n-1),即两段内存有重合,并且要被拷贝的数据地址(from)在要拷贝到的地址(to)的前面,这时:
从 头部 from 开始拷贝到尾部 from+(n-1),
即 *from->*to , *(from+1)->*(to+1) ... *(from+(n-1)) -> *(to+(n-1))
结果:array[8]={0,1,0,1,0,1,2,7}; 不是我们期待的结果(第三个0是错误拷贝结果)。
或者
从尾部from+(n-1) 开始拷贝到 from ,
即*(from+(n-1)) -> *(to+(n-1)) ... *(from+1)->*(to+1) ,*from->*to。
结果:array[8]={0,0,1,2,0,1,2,7};是我们期待的结果。换句话说你只可以反向拷贝。
情形3:【举例 from = arrary+2,to=array,n=3】
to < from< to +(n-1),即两段内存有重合,并且要被拷贝的数据地址(from)在要拷贝到的地址(to)的后面,这时:
从 头部 from 开始拷贝到尾部 from+(n-1),
即 *from->*to , *(from+1)->*(to+1) ... *(from+(n-1)) -> *(to+(n-1)) ;
结果:array[8]={2,3,4,3,4,5,6,7};是我们期待的结果。
或者
从尾部from+(n-1) 开始拷贝到 from ,
即*(from+(n-1)) -> *(to+(n-1)) ... *(from+1)->*(to+1) ,*from->*to。
结果:array[8]={4,3,4,3,4,5,6,7};不是我们期待的结果(第一个4是错误拷贝结果)。换句话说你只可以正向拷贝。
在纸上画一下图,把三种情形都表示出来,然后考虑两种拷贝顺序(从头开始和从尾部开始),一共 3*2 六种情况,就明白了。
以上所有的讨论是基于to和from所指空间的大小都大于等于n。
void MemCopy(void* from,void* to,int n)
{
if(from==NULL||to==NULL||n<0)
return;
int i;
if(from<=to)
{
for(i=n-1;i>=0;i--)
*((char *)(to+i)) = *((char *)(from+i));
}
else
{
for(i=0;i<n;i++)
*((char*)(to+i)) = *((char*)(from+i));
}
}
分析:这个有个陷阱就是 to 和 from 两个地址谁大谁小,以及是否重叠。我们假设两段内存地址范围为: from ~ from +(n-1), to ~ to+(n-1)(长度都为 n ),为了说明下面的情形,我们举一个例: 数组 array[8]={0,1,2,3,4,5,6,7};
情形1:【举例 from = array,to = arary+4 ,n=3】 from +(n-1) < to 或者 to+(n-1) < from ,即两段内存没有任何重合,这时 从 头部 from 开始拷贝到尾部 from+(n-1), 即 *from->*to , *(from+1)->*(to+1) ... *(from+(n-1)) -> *(to+(n-1)) 或者 从尾部from+(n-1) 开始拷贝到 from , 即*(from+(n-1)) -> *(to+(n-1)) ... *(from+1)->*(to+1) ,*from->*to 都是没有问题。结果都是 :array[8]={0,1,2,3,0,1,2,7}; 换句话说你可以正向拷贝,也可以反向拷贝。
情形2:【举例 from = arrary,to=array+2,n=3】 from < to <from +(n-1),即两段内存有重合,并且要被拷贝的数据地址(from)在要拷贝到的地址(to)的前面,这时: 从 头部 from 开始拷贝到尾部 from+(n-1), 即 *from->*to , *(from+1)->*(to+1) ... *(from+(n-1)) -> *(to+(n-1)) 结果:array[8]={0,1,0,1,0,1,2,7}; 不是我们期待的结果(第三个0是错误拷贝结果)。 或者 从尾部from+(n-1) 开始拷贝到 from , 即*(from+(n-1)) -> *(to+(n-1)) ... *(from+1)->*(to+1) ,*from->*to。 结果:array[8]={0,0,1,2,0,1,2,7};是我们期待的结果。换句话说你只可以反向拷贝。
情形3:【举例 from = arrary+2,to=array,n=3】 to < from< to +(n-1),即两段内存有重合,并且要被拷贝的数据地址(from)在要拷贝到的地址(to)的后面,这时: 从 头部 from 开始拷贝到尾部 from+(n-1), 即 *from->*to , *(from+1)->*(to+1) ... *(from+(n-1)) -> *(to+(n-1)) ; 结果:array[8]={2,3,4,3,4,5,6,7};是我们期待的结果。 或者 从尾部from+(n-1) 开始拷贝到 from , 即*(from+(n-1)) -> *(to+(n-1)) ... *(from+1)->*(to+1) ,*from->*to。 结果:array[8]={4,3,4,3,4,5,6,7};不是我们期待的结果(第一个4是错误拷贝结果)。换句话说你只可以正向拷贝。
在纸上画一下图,把三种情形都表示出来,然后考虑两种拷贝顺序(从头开始和从尾部开始),一共 3*2 六种情况,就明白了。
以上所有的讨论是基于to和from所指空间的大小都大于等于n。void MemCopy(void* from,void* to,int n) { if(from==NULL||to==NULL||n<0) return; int i; if(from<=to) { for(i=n-1;i>=0;i--) *((char *)(to+i)) = *((char *)(from+i)); } else { for(i=0;i<n;i++) *((char*)(to+i)) = *((char*)(from+i)); } }