这两个函数都是将s2指向位置的n字节数据拷贝到s1指向的位置。memcpy 假定两块内存区域没有数据重叠,而 memmove 没有这个前提条件。如果复制的两个区域存在重叠时使用memcpy,其结果是不可预知的( 未定义的 ),有可能成功也有可能失败。原因如下:

  • 当src地址大于dest地址时,即使有重叠,也可以正常复制。

  • 当src地址小于dest地址时,若重叠,则复制发生异常。

    这种情况下,src的地址小于dest的地址,拷贝前3个字节没问题,但是拷贝第4,5个字节时,原有的内容已经被src拷贝过来的字符覆盖了,所以已经丢失原来src的内容。

    然而,vs2019下进行试验,发现即使是在第二种情况下,memcpy与memmove的结果相同,都正确,原因可能是 memcpy 目前也采用了 memmove 的方式。

memmove的实现:

void *memmove(void *dest, const void *src, size_t n)
{
    unsigned char *pd = dest;
    const unsigned char *ps = src;
    if((ps + n > pd)&& ps < pd)
        for (pd += n, ps += n; n--;)
            *--pd = *--ps;
    else
        while(n--)
            *pd++ = *ps++;
    return dest;
}

memmove会对拷贝的数据作检查,确保内存没有覆盖,如果发现会覆盖数据,简单的实现是调转开始拷贝的位置,从尾部开始拷贝但是实际在 C99 实现中,是将内容拷贝到临时空间,再拷贝到目标地址中

void *memmove(void *dest, const void *src, size_t n)
{
 unsigned char tmp[n];
 memcpy(tmp,src,n);
 memcpy(dest,tmp,n);
 return dest;
}

memcpy的实现:

void *memcpy(void *dest, const void *src, size_t n)
{
    char *dp = dest;
    const char *sp = src;
    while (n--)
        *dp++ = *sp++;
    return dest;
}
//目前memcpy已做改进,以下为源码:
void *memcpy(void *dst, const void *src, size_t len)
{
	if(NULL == dst || NULL == src){
		return NULL;
	}
	
	void *ret = dst;
	
	if(dst <= src || (char *)dst >= (char *)src + len){
		//没有内存重叠,从低地址开始复制
		while(len--){
			*(char *)dst = *(char *)src;
			dst = (char *)dst + 1;
			src = (char *)src + 1;
		}
	}else{
		//有内存重叠,从高地址开始复制
		src = (char *)src + len - 1;
		dst = (char *)dst + len - 1;
		while(len--){
			*(char *)dst = *(char *)src;
			dst = (char *)dst - 1;
			src = (char *)src - 1;
		}
	}
	return ret;
}
文章作者: 极简
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 后端技术分享
C/C++
喜欢就支持一下吧