C语言:模拟实现memmove函数

首先模拟实现memmove函数之前,我们需要先明白这个函数的功能和用法:memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。也就是说,他可以处理memcpy函数不能处理的如果拷贝时出现重叠的情况。那么下面我们就来具体说说怎么模拟实现memmove函数。

我们要知道当内存拷贝时分为两种情况,看下面这幅图
这里写图片描述
如图片中所看到的,第一种情况是当目标指针变量dest的起始位置小于源指针变量src的起始位置,这时候我们需要将src指向的内容正着拷贝到dest所指向的空间,即从左向右依次拷贝。第二种情况是当目标指针变量dest的起始位置大于源指针变量src的起始位置,需要将src指向的内容倒着拷贝到dest所指向的空间,即从右向左依次拷贝。如果不这样,很有可能产生覆盖。

下面是我们的具体实现代码,里面有比较详细的解释,供大家参考:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

/*基本思想:为了避免重叠,分为两种情况。如果目标指针变量dest的起始值小于源指针变量src的起始值,那么正着拷贝,即
            从左向右拷贝。如果目标指针变量dest的起始值大于源指针变量src的起始值,那么倒着拷贝,即从右向左拷贝*/

void* my_memmove(void* dest, const void*src, int count)//因为是内存拷贝函数,所以什么类型都需要接收,所以此处用void*做函数返回值类型和参数类型
{
    assert(dest != NULL);
    assert(src != NULL);
    void* ret = dest;//因为dest在循环体内是要变化的,所以定义一个临时指针变量来存放dest的地址。dest变化会引起ret变化,但ret变化不会引起dest变化
    if (dest < src)//如果dest的起始位置小于src的起始位置,那么从左向右进行拷贝
    {
        while (count--)
        {
            *(char*)dest = *(const char*)src;//因为void*类型不能进行解引用操作,所以要进行强制类型转化,并且count是代表的是字节数,一个字节一个字节的拷贝,所以强制转化为char*类型
            ++(char*)dest;//因为void*类型不能进行++运算,所以进行强制类型转化,并且如果是后置++,那么强制类型转化会先对dest进行,所以要进行前置++
            ++(char)src;
        }
    }
    else
    {
        *((char*)dest + count) = *((const char*)src + count);//从右向左拷贝
        --(char*)dest;
        --(char*)src;
    }
    return ret;
}
//打印输出arr函数
void print(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}
int main()
{
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    my_memmove(arr, arr + 3, 16);//此处的16为字节数 
    int sz = sizeof(arr) / sizeof(arr[0]);
    print(arr, sz);
    system("pause");
    return 0;
}

运行的结果如下图:
这里写图片描述

最后再说几点需要注意的地方:

1.dest和src指针变量需要断言,不能为空。并且源指针变量src是不改变的,所以要用const修饰,以起到保护作用。
2.因为是内存拷贝函数,所以函数的返回值类型和参数类型都必须可以接收任何类型,所以要使用void*做函数的返回值类型和参数类型。
3.void*类型是不能进行解引用操作和++、–运算的,所以在进行解引用操作和++、–运算之前要进行强制类型转换,并且,memmove函数是一个字符一个字符拷贝,所以这里要强制类型转换为char*类型。
4.因为目标指针变量dest在循环体内是要发生改变的,所以定义一个临时指针变量来存放dest的地址,此后dest变化临时指针变量就会变化,但临时指针变量变化不会引起dest变化,最后函数返回临时指针变量即可。

猜你喜欢

转载自blog.csdn.net/windyj809/article/details/80052974