目录
摘要:
一:memmove的使用
1:不重叠
2: 重叠情况1
3: 重叠情况2
二:memmove的实现
1:情况分析
2:规律总结
3:实现代码
摘要:
本文介绍并实现memmove~
一:memmove的使用
void * memmove ( void * destination, const void * source, size_t num );
参数解释:
dest:目标内存地址的指针,数据要复制到的位置src:源内存地址的指针,数据来源的位置n:要复制的字节数
函数功能:
将src指向的内存区域中的n个字节复制到dest指向的内存区域。
返回值:
返回dest的指针(目标内存区域的首地址)
注意事项:
①:主要用于内存重叠场景
当
dest和src有重叠时,memmove是安全的选择
②:性能略低于memcpy
因为
memmove需要判断是否重叠,可能牺牲少量性能不重叠时优先用
memcpy,重叠时用memmove
使用示例:
1:不重叠
#include <stdio.h> #include <string.h> int main() { int src[] = {1, 2, 3, 4, 5}; int dest[5]; memmove(dest, src, sizeof(src)); printf("整型数组不重叠复制:"); for (int i = 0; i < 5; i++) printf("%d ", dest[i]); printf("\n"); return 0; }解释:不重叠的情况下,是等同于memcpy的
2: 重叠情况1
#include <stdio.h> #include <string.h> int main() { int arr[] = { 10, 20, 30, 40, 50 }; // 将 [30,40,50] 移动到 [10,20,30] 的位置 memmove(arr, arr + 2, 3 * sizeof(int)); printf("整型数组向左重叠移动:"); for (int i = 0; i < 5; i++) printf("%d ", arr[i]); printf("\n"); return 0; }解释:重叠情况1,就是指针src 小于 指针desr ,memmove可以应对
3: 重叠情况2
#include <stdio.h> #include <string.h> int main() { int arr[] = { 10, 20, 30, 40, 50 }; // 将 [10,20,30] 移动到 [20,30,40] 的位置 memmove(arr + 2, arr, 3 * sizeof(int)); printf("整型数组向右重叠移动:"); for (int i = 0; i < 5; i++) printf("%d ", arr[i]); printf("\n"); return 0; }解释:重叠情况2,就是指针src 大于 指针desr ,memmove也可以应对
二:memmove的实现
void * memmove ( void * destination, const void * source, size_t num );
对于不重叠的情况,我们不需要去讨论src和dest的大小关系,都可以直接复制
我们知道,复制的方式无非就是从前往后复制 或 从后往前复制 这两种方法
对于重叠的情况,则需要根据src和dest的大小关系分开讨论,寻找适配的复制方法!
1:情况分析
a:src 大于 dest
解释:当src 大于 dest的时候,如果对src部分从后往前复值,则一定出错!按照8,7,6,5,4的顺序复制给5,4,3,2,1,无法得到4,5,6,7,8,6,7,8,9,10(正确)而是得到7,8,6,7,8,6,7,8,9,10(错误)
而从前往后赋值,则正确:
b:src 小于 dest
解释:当src 小于 dest的时候,若是从前往后复制,就是按照4,5,6,7,8的顺序复制给6,7,8,9,10,无法得到1,2,3,4,5,4,5,6,7,8(正确),而是得到1,2,3,4,5,4,5,4,5,4(错误)
而从后往前赋值,则正确:
2:规律总结
总结:
①:重叠情况,若src>dest,则从前往后进行复制;若src<dest,则从后往前复制
②:不重叠情况,则任意选择
③:统一 src>dest,前往后复制,src<dest,后往前复制
更深层次的理解:
当src(源)<dest(目的)的时候,只能后往前!
因为src后部分和dest的前部分重叠,此时采取错误的前往后拷贝,则会让src后部分还没被拷贝的元素被更新赋值了,等到之后要拷贝src后部分的元素则肯定错
当src(源)>dest(目的)的时候,只能前往后!
因为src前部分和dest的后部分重叠,此时采取错误的后往前拷贝,则会让src前部分还没被拷贝的元素被更新赋值了,等到之后要拷贝src前部分的元素则肯定错
3:实现代码
#include <assert.h> void *my_memmove(void *dest, const void *src, size_t num) { void *ret = dest; assert(dest && src); if (src > dest) { // 内存不重叠或 dest 在前:从前往后复制 while (num--) { *(char *)dest = *(char *)src; dest = (char *)dest + 1; src = (char *)src + 1; } } else { // 内存重叠且 dest 在后:从后往前复制 while (num--) { *((char *)dest + num) = *((char *)src + num); } } return ret; }解释:
①:首先 肯定是需要用ret保存一下dst指针的,用于最后的返回,因为dst会在后面的代码中改变
②:断言检测dst和src指针,其一为空,则代表用于传参错误,直接报错
③:判断情况,若为src > dest ,则采用前往后复制,esle采取后往前复制;
④:前往后复制就是类似memcpy的强转为char*之后,直接num次循环逐字节复制;而后往前复制,则强转char*之后,需要让指针取指向最后一个元素的最后一个字节,然后往前逐字节复制!而while (num--)这一步,不仅循环了num次控制次数,并且让num在第一个进入循环就减1了,此时直接让强转为char*的指针+num即可指向最后一个元素的最后一个字节
📌 [ 作者 ] shylyly
📃 [ 首次发布 ] 2024.3.20
❌ [ 最新修改 ] 2026.10.14
📜 [ 声明 ] 由于笔者水平有限,文中难免有疏漏或不妥之处,还望读者不吝赐教。