ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

memmove的模拟实现

memmove的模拟实现

目录

摘要:

一: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
📜 [ 声明 ] 由于笔者水平有限,文中难免有疏漏或不妥之处,还望读者不吝赐教。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进