C语言-内存函数的实现(二)之memmove

作者: 博客园精华区  更新时间:2021-04-27 23:36:00  原文链接


C语言中的内存函数有如下这些

  • memcpy
  • memmove
  • memcmp
  • memset

下面看看memmove函数

memmove

为什么会需要memmove函数?

int main() 
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    // 想把12345 拷贝到 34567上去
    // 应该打印 1 2 1 2 3 4 5 8 9 10
    my_memcpy(arr + 2, arr, 20);

    for (i = 0; i < 10; i++) 
    {
    	printf("%d ", arr[i]);
    }
    // 但是输出 1 2 1 2 1 2 1 8 9 10

    return 0;
}

上面会输出 1 2 1 2 1 2 1 8 9 10,我们来看看为什么会出现这样的结果。

我这里画了张图,方便理解。

因为拷贝的地方重叠了,使原来的数据(3 4 5)被覆盖了,导致最后出来的结果不是我们想要的。

也就是说,如果拷贝的地方重叠了,那么就会出现这种情况。

那么如何解决呢?答案就是从后往前拷贝,指针从最后的地方开始操作。

还是上一张图

这样,就得出了我们想要的结果。

但是呢,也不能一概而论,就全部都是从后往前拷贝,还是得分情况的,具体就是看 destinationsource 的位置关系。

回到最开始的问题, 为什么会需要memmove函数? ,因为memmove这个函数可以处理这种重叠拷贝。

老规矩,我们还是看看文档是怎样说的,如下

memmove文档

void * memmove ( void * destination, const void * source, size_t num );

Move block of memory

移动内存块(移动内存数据)

Copies the values of num bytes from the location pointed by source to the memory block pointed by destination . Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.

从source(源内存块位置)直接指向的地方开始复制num个字节的数据到destination指向的内存块位置。然后复制就像使用了中间缓冲区一样,允许destination和source重叠。

The underlying type of the objects pointed by both the source and destination pointers are irrelevant for this function; The result is a binary copy of the data.

这句话没看懂,不影响我们学这个。

The function does not check for any terminating null character in source - it always copies exactly num bytes.

这个函数不会检查'\0',不会遇到'\0'就停下来,它就只认识要复制的num个字节数据。

To avoid overflows, the size of the arrays pointed by both the destination and source parameters, shall be at least num bytes.

为了避免溢出,这两个数组的大小至少为num个字节。

可以看出,memmove和memcpy的唯一区别就是,memmove函数处理的源内存块和目标内存块是可以重叠的。

也就是说,如果源空间和目标空间出现重叠,就得使用memmove函数处理。

实现

断言指针不为空是个好习惯~

void* my_memmove(void* dest, void* src, size_t num) 
{
    //dest落在了src的左边,从前往后拷贝
    //dest落在了src的右边,同时没有超过那个重叠的边界的时候,从后往前拷贝
    assert(dest != NULL);
    assert(src != NULL);
    void* rest = dest;
    // void* 不能直接解引用,那么如何复制呢?
    // 给了num个字节,也就是需要复制num个字节
    // 那就转换成char*,一个一个字节的复制过去
    if (dest < src) 
    //if (dest < src || dest > (char*)src + num) 
    {
    	//dest落在了src的左边,从前往后拷
    	while (num--)
    	{
    	    *(char*)dest = *(char*)src;
    	    //++(char*)dest;
    	    //++(char*)src;
    	    ((char*)dest)++;
    	    ((char*)src)++;
    	}
    }
    else 
    {
    	// 从后往前拷
    	// 找到最后一个字节
    	while (num--) 
    	{
    	    *((char*)dest + num) = *((char*)src + num);
    	}

    }
    return rest;
}

最后,感谢 屏幕前靓仔 花宝贵的时间阅读我这篇博客~

本人水平有限,如有错误以及不足之处,欢迎 靓仔 指出。