C语言模拟实现memmove的示例代码

2023-02-26,,

目录
  • 前言
    • 例子
  • memmove的模拟实现
    • 具体实现步骤
      • 总结

        前言

        上一篇我们介绍了memcpy和strcpy的区别,以及memcpy模拟实现,但这两个库函数都有一个缺点,那就是不能自己复制自己的内容

        例子

        这有一个数组arr,其元素分别为1、2、3、4、5、6、7、8、9、10,我们想将1、2、3、4复制到2的后面,从而将数组arr变成1、2、1、2、3、4、7、8、9、10

        用memcpy尝试

        我们发现,跟我们预期的 1、2、1、2、3、4、7、8、9、10有出入。

        错误原因

        int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
        	memcpy(arr + 2, arr, 16);

        memcpy实现过程

        void* my_memcpy(void* dest, void* src, size_t count)
        {
            void* ret = dest;
            while (count--)
            {
                *(char*)dest = *(char*)src;
                dest = (char*)dest + 1;
                src = (char*)src + 1;
            }
            return ret;
        } 

        当我们这样操作时,src一开始指向1,1被复制到了3的位置上,后面指向2,2被复制到4的位置上,当src指针指向原来的3时,指向的内容变成了1,又将1复制到了5的位置上,当src指向原来的4时,指向的内容变成了2,又将2复制到了6的位置上。所以就出错了。

        用memmove尝试

        我们发现目的达到了,说明memmove适用于内存发生重叠的情况。那么memmove是怎么实现的呢?

        memmove的模拟实现

        整段代码

        void* my_memmove(void* dest, void* src, size_t count)
        {
        	void* ret = dest;
        	if (dest < src)
        	{
        		while (count--)
        		{
        			*(char*)dest = *(char*)src;
        			dest = (char*)dest + 1;
        			src = (char*)src + 1;
        		}
        	}
        	else
        	{
        		while (count--)
        		{
        			*((char*)dest + count) = *((char*)src + count);
        		}
        	}
        	return ret;
        }

        memmove的定义

        由图可知,memmove的返回值是目标地址,形势参数分别是(目标地址,源头地址,需要操作的字节数)

        具体实现步骤

        第一种情况(dest在src后),采用由后向前复制

        由上面的错误分析,我们知道是因为后面要被复制过去的内容被更改了,还是用上面的例子做示范,我们从1开始复制的话,1会将3覆盖掉,进而导致想将3复制到到5的位置上时,实际上是将1复制到5的位置上。

        那么我们如果从4开始复制呢?我们由后至前进行复制,将4复制到6,再将3复制到5,这样我们就不怕3、4被1、2覆盖掉了。

        我们要想先将4复制到6,先得将src指向4,dest指向6,然后再进行交换。我们用加传过去的字节数来实现。

        *((char*)dest + count) = *((char*)src + count);

        我们想将指针前移,直接count减一就行,又因为我们要重复这一行为,所以我们使用while循环来实现。

                while (count--)
        		{
        			*((char*)dest + count) = *((char*)src + count);
        		}

        第二种情况(dest在src前),采用由前向后复制

        如果dest在src前,我们还能用由后至前的方法复制吗?

        例如,我们将3、4、5、6向前移动2次,也就是将1、2、3、4、5、6、7、8、9、10变成3、4、5、6、5、6、7、8、9、10

        我们发现并没有成为我们想象当中的样子。

        错误原因

        依然是要被复制的内容在被复制之前就被更改了,这里先将6移动到4,5移动到3,想将4移动到2时,实际上复制过去的是6,以此类推...

        所以我们要采用由前向后复制的方法(【C语言】字符串拷贝函数(strcpy)与内存拷贝函数的不同及内存拷贝函数(memcpy)的模拟实现一文里有)

        总结

        memcpy不能实现自己拷贝自己,也就是不适用于内存叠加的情况。我们用memmove便可以解决这个问题。

        对于memmove的模拟实现,核心思想就是将会被覆盖的、要被复制的内容提前使用。这里分为两种情况,一种是被复制的内容地址在目标内容地址的前面,另一种就是被复制的内容地址在目标内容地址的后面,对于前一种情况,我们使用由后至前进行复制的方法,对于后一种情况,我们使用由前至后进行复制的方法(dest在src后就由后向前。dest在src前,就由前向后)。

        原文地址:https://blog.csdn.net/ZHENGZJM/article/details/128471331