【C标准库】深入理解 C 语言memmove函数:安全内存拷贝的利器


🌈个人主页:聆风吟_
🔥系列专栏:C语言标准库
🔖少年有梦不应止于心动,更要付诸行动。


文章目录

📋前言

在 C 语言内存操作中,memcpymemmove 都是用于内存拷贝的函数,但很多初学者容易混淆二者的区别,甚至误用 memcpy 导致程序出现诡异 Bug。今天我们就重点拆解 memmove 函数 ,搞懂它的用法、原理、与 memcpy 的核心区别,让你在开发中彻底避开内存拷贝的坑。


一、memmove函数是什么?

memmove 是 C 标准库 <string.h> 中提供的内存拷贝函数 ,全称是 memory move(内存移动),作用是将一段内存的数据拷贝到另一段内存中。

它的核心优势:支持内存区域重叠 的安全拷贝,这是 memcpy 做不到的。


二、函数原型与参数解析

1.1 头文件

使用前必须包含:

c 复制代码
#include <string.h>

1.2 函数原型

c 复制代码
void *memmove(void *dest, const void *src, size_t n);

1.3 参数说明

参数 含义
dest 目标内存地址(拷贝数据的目的地)
src 源内存地址(要拷贝的原始数据)
n 要拷贝的字节数(无符号整数)

1.4 返回值

返回目标内存的起始地址dest


三、memmove的核心用法

memmove 的使用场景和 memcpy 几乎一致,唯一的区别是:当源内存和目标内存存在重叠时,必须用 memmove

基础示例:普通内存拷贝

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char src[] = "Hello, memmove!";
    char dest[50] = { 0 };  // 初始化目标数组

    // 拷贝 src 中的所有数据(包含结束符)
    memmove(dest, src, strlen(src) + 1);

    printf("源字符串:%s\n", src);
    printf("目标字符串:%s\n", dest);
    return 0;
}

输出结果

复制代码
源字符串:Hello, memmove!
目标字符串:Hello, memmove!

这个例子中内存不重叠,memmovememcpy 效果完全相同。


四、关键:memmove与memcpy的区别

这是面试和开发中最常考、最容易踩坑的点,必须吃透!

4.1 核心区别

  • memcpy不检查内存重叠 ,直接从前往后拷贝,重叠时会导致数据覆盖、结果错误
  • memmove会处理内存重叠 ,根据内存地址自动选择拷贝方向,拷贝结果永远安全

4.2 什么是内存重叠?

举个直观的例子:

有一个数组 arr[10] = {1,2,3,4,5,6,7,8,9,10},我们想把前5个元素(1-5) 拷贝到 从第3个位置开始 的区域:

此时源内存和目标内存部分重叠 ,用 memcpy 会出错,用 memmove 则安全。

4.3 实战对比:重叠内存拷贝

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int len = sizeof(arr) / sizeof(arr[0]);

    // 场景:把 arr[0]~arr[4] 拷贝到 arr[2]~arr[6](内存重叠)
    // 用 memcpy(错误示例)
    // memcpy(arr+2, arr, 5*sizeof(int));

    // 用 memmove(正确示例)
    memmove(arr + 2, arr, 5 * sizeof(int));

    // 打印结果
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

memmove 正确输出

复制代码
1 2 1 2 3 4 5 8 9 10

memcpy 错误输出

复制代码
1 2 1 2 1 2 1 8 9 10

原因:memcpy 从前往后拷贝,刚拷贝的新数据覆盖了源数据,导致后续拷贝拿到的是错误值。


五、memmove底层实现原理

为什么 memmove 能处理重叠内存?核心是拷贝方向判断

  1. 比较 destsrc 的地址大小;
  2. 如果 destsrc 前方 → 从前往后拷贝;
  3. 如果 destsrc 后方 → 从后往前拷贝;

这样就能避免重叠区域的数据被提前覆盖。

手写简易版 memmove(理解原理):

c 复制代码
#include <stdio.h>

// 自定义 memmove 函数
void* my_memmove(void* dest, const void* src, size_t n)
{
    // 转换成 char 指针,以便按字节操作
    unsigned char* d = (unsigned char*)dest;
    const unsigned char* s = (const unsigned char*)src;

    // 判断是否有重叠,以及 dest 是否在 src 之后
    if (d > s && d < s + n)
    {
        // 情况 B:重叠且 dest 在 src 右侧
        // 必须从后往前复制
        for (size_t i = n; i > 0; i--)
        {
            d[i - 1] = s[i - 1];
        }
    }
    else
    {
        // 情况 A:无重叠 或 dest 在 src 左侧
        // 从前往后复制
        for (size_t i = 0; i < n; i++)
        {
            d[i] = s[i];
        }
    }
    return dest;
}

// 测试代码
int main()
{
    char str[] = "Hello, World!";

    // 将 "World" 移动到 "Hello" 的位置(重叠移动)
    my_memmove(str, str + 7, 5); // 移动 "World"
    printf("%s\n", str); // 输出 "World, World!"


    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    my_memmove(arr + 4, arr + 2, 4 * sizeof(int));

    for (int i = 0; i < 8; i++)
    {
        printf("%d ", arr[i]);
    }
    // 输出: 1 2 3 4 3 4 5 6

    return 0;
}

这个简易实现完美还原了 memmove 的核心逻辑,看懂它就彻底理解了函数原理。


六、使用memmove的注意事项

  1. 必须保证目标内存空间足够

    目标内存 dest 必须有至少 n 字节的可用空间,否则会造成内存越界,导致程序崩溃。

  2. 参数 n 是字节数,不是元素个数

    拷贝数组时,要计算总字节数:元素个数 × 单个元素大小,例如:

    c 复制代码
    // 拷贝5个int类型数据
    memmove(dest, src, 5 * sizeof(int));
  3. srcconst 修饰

    标准库中 srcconst 指针,保证源数据不会被修改,这是安全编程的规范。

  4. 优先使用 memmove

    除非你能100%确定内存不重叠,否则日常开发中直接用 memmove 替代 memcpy,避免潜在的重叠风险。


📝全文总结

  1. memmove<string.h> 中的安全内存拷贝函数,支持内存重叠场景;
  2. 函数原型:void *memmove(void *dest, const void *src, size_t n);
  3. memcpy 核心区别:memmove 处理重叠内存,memcpy 不处理;
  4. 底层原理:根据地址大小自动选择从前往后从后往前拷贝;
  5. 开发建议:不确定内存是否重叠时,优先使用 memmove,更安全。

掌握 memmove,就能彻底解决 C 语言中内存拷贝的重叠问题,写出更健壮的代码。如果觉得本文对你有帮助,欢迎点赞收藏~

相关推荐
临溟夜空的繁星2 小时前
C++STL—— list
开发语言·c++·list
lsx2024062 小时前
Ruby JSON处理指南
开发语言
busideyang2 小时前
函数指针类型定义笔记
c语言·笔记·stm32·单片机·算法·嵌入式
深邃-2 小时前
数据结构-双向链表
c语言·开发语言·数据结构·c++·算法·链表·html5
2401_878530212 小时前
分布式任务调度系统
开发语言·c++·算法
愤豆2 小时前
06-Java语言核心-JVM原理-JVM内存区域详解
java·开发语言·jvm
wzhidev2 小时前
04、Python核心数据类型详解:从一段诡异的调试说起
开发语言·python
luanma1509802 小时前
Laravel 7.X核心特性深度解析
android·开发语言·php·lua·laravel
@haihi2 小时前
ESP32 MQTT示例解析
开发语言·网络·mqtt·github·esp32