作者:~小明学编程
文章专栏:C语言基础知识
格言:目之所及皆为回忆,心之所想皆为过往
目录
今天带大家深入的了解memcpy和memmove这两个函数,并且带大家实现这两个函数。
memcpy是c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
1.复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2.复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3.用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
char *strcpy( char *strDestination, const char *strSource );
这是我们strcpy函数的各个参数,我们可以看到该函数的参数是char*所以只能用于字符串的拷贝,当我们有其它的需求的时候例如我们想要拷贝整型数据浮点型的数据又或者是结构体的时候那么该怎么拷贝呢?
显然我们是不能继续再使用strcpy了,这时就引入了我们的memcpy函数。

我们先看一下memcpy的各个参数,这里我们看到其的返回类型是void*类型,三个参数分别是两个void*类型和一个size_t类型因为其参数是void*的类型所以我们可以接受各个类型的数据,就不会再拘泥于一种字符类型数据的拷贝。
- struct S
- {
- char name[10];
- int age;
- };
- int main()
- {
- struct S arr1[] = {{"ajd",20},{"qwe",18}};
- struct S arr2[3] = { 0 };
- memcpy(arr2, arr1, sizeof(arr1));
- return 0;
- }
这是一个结构体类型的数据,然后我们用memcpy函数对其进行拷贝。

这里我们可以看到拷贝成功。
那么我们如何模拟实现这个函数呢?
前面我们已经了解了memcpy的实现机制和参数部分,下面我们要开始自己着手去实现它,
首先我们知道memcpy拷贝的是字节那么我们就将其强转为char*的类型,然后再将我们的地址向后移动,依次移动我们的count个字节。
- void* my_memcpy(void* dest, const void* src, size_t num)
- {
- assert(dest && src);
- void* ret = dest;
- while (num--)
- {
- *(char*)dest = *(char*)src;
- ++(char*)dest;
- ++(char*)src;
- }
- return ret;
-
- }
- struct S
- {
- char name[10];
- int age;
- };
- int main()
- {
- struct S arr1[] = {{"ajd",20},{"qwe",18}};
- struct S arr2[3] = { 0 };
- my_memcpy(arr2, arr1, sizeof(arr1));
- return 0;
- }
代码的实现如上。

我们也成功的将该结构体给拷贝了。
- int main()
- {
- int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
- my_memcpy(arr1+2, arr1, 20);
- for (int i = 0; i < 10; i++)
- {
- printf("%d ", arr1[i]);
- }
- return 0;
- }
上面一段代码我们的目的是想把1~5的数字共20个字节拷贝到原来3~7的数字的位置上

然后我们看到其打印的结果和我们所想到的并不一致我们想要的是1 2 1 2 3 4 5 8 9 10
这是为什么呢?

这里我们就看出来问题了,我们把1和2拷贝到了3和4的位置,但是当我们想拷贝3的时候发现3已经被原来的1给覆盖了所以才会出现12121这种情况,那么如何避免这种情况呢?
这时候就要看我们的memmove函数了。

我们可以看到memmove函数完美的帮我们给解决了这个问题。

我们可以看到memcpy和memmove在参数上的主要区别就是其参数的类型,memmove可以接受任何类型的数据,那么具体该怎么实现呢?
memmove函数解决了我们先前的拷贝覆盖问题,那么我们要想模拟实现这个函数就得解决上面的问题。
当我们拷贝时地址有重叠我们从前向后拷贝的话就不行,那么我们可以考虑一下从后向前来拷贝数据。

我们看到这样就解决了上面的问题了,当我们拷贝的二者地址之间相差超过count时我们就无需担心覆盖的问题了。
总结:我们dest的地址小于src的地址时我们就采用从前向后拷贝,当我们dest的地址大于src但是小于src+count的地址我们采用的从后向前拷贝,当我们的dest大于src+count的地址的时候从前从后拷贝都可以。
所以当我们模拟实现这个函数的时候,就把它分成两种情况而定dest
讨论就行了。
- void* my_memmove(void* dest, const void* src, size_t count)
- {
- assert(dest && src);
- char* ret = (char*)dest;
- if (dest < src)
- {
- while (count--)
- {
- *(char*)dest = *(char*)src;
- src = (char*)src + 1;
- dest = (char*)dest + 1;
- }
- }
- else
- {
- while (count--)
- {
- *((char*)dest+count) = *((char*)src+count);
- }
- }
- return ret;
- }
- int main()
- {
- int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
- my_memmove(arr1+2, arr1, 20);
- for (int i = 0; i < 10; i++)
- {
- printf("%d ", arr1[i]);
- }
- return 0;
- }
上面就是模拟实现的代码

这里也解决了上述的问题。