函数原型:void* memcpy(void* dst,const void* src,size_t size);
函数作用:将src所指的内存单元拷贝到dst所指的内存单元,一共拷贝size个字节。如果src和dst存在重叠区域,其行为是未定义的。但是可以肯定的是,无法完整的把src的内存区域拷贝到dst,dst区域中将会存在重复的元素。
注:所谓的重叠是指dst指向src内存区域中的某个单元,或者src指向dst中的某个单元
例:int src[10];int* dst=src+5; 或者 int dst[10];int* src=dst+5;
则称src和dst之间则存在重叠区域
所以,如果自己编写一个memcpy()函数,且保证要能完整的将源区域元素拷贝到目标区域。则需要处理发生重叠区域的特殊情况。因此,新memcpy()函数需要实现两个功能。第一,无重叠区域,直接拷贝;第二,存在重叠区域,进行相应的处理。
接下来要思考的则是如何判断两者之间是否存在重叠区域呢?
首先,思考一下我们是如何内存拷贝的。内存拷贝既可以从前往后也可以从后往前进行拷贝。
然后观察一下存在内存重叠区域时的情况。可以发现内存重叠时存在两种情况:
- dst<=src<dst+size;即src的内存区域在dst区域的后半部分,这种情况直接可以从前往后进行拷贝。
- src<=dst<src+size;即dst的内存区域在src区域的后半部分,这种情况则需从后往前进行拷贝。
之后再次简化,可以发现只要dst<=src,直接从前往后拷贝;只要src<dst,则可以直接从后往前拷贝。这样就能完整的将src内存块拷贝到dst中。
为化简的流程
化简流程图后可得
因此可得:
void* memcpy_s(void* dst,const void* src,size_t size){
if(!src||!dst)
return 0;
char* tmp=(char*)dst;
const char* s=(char*)src;
//存在重叠区域:src<=dst<src+size,从后往前拷贝
if(s<=dst&&dst<s+size){
while(size--){
*(tmp+size)=*(s+size);
}
}
//其它所有情况直接从前往后
else{
while(size--)
*tmp++=*s++;
}
return dst;
}
Linux上的memmove()
void *memmove(void *dest, const void *src, size_t count)
{
char *tmp;
const char *s;
//只要目的地的起始地址小于源地址,从前往后
if (dest <= src) {
tmp = dest;
s = src;
while (count--)
*tmp++ = *s++;
} else {
tmp = dest;
tmp += count;
s = src;
s += count;
while (count--)
*--tmp = *--s;
}
return dest;
}