现在位置: 首页 > C 教程 > 正文

C 库函数 - memmove()

C 标准库 - string.h C 标准库 - string.h


memmove() 是 C 语言标准库 <string.h> 中的内存操作函数,用于将指定数量的字节从源地址复制到目标地址,并能安全处理内存重叠的情形。

单词释义memmovememory move(内存移动)的缩写,表示在内存中移动数据。

C 库函数 void *memmove(void *str1, const void *str2, size_t n)str2 复制 n 个字节到 str1

与 memcpy() 的区别

  • 当目标区域与源区域存在内存重叠时,memmove() 能保证在源数据被覆盖之前,先将重叠部分的字节安全地转移到目标区域,因此比 memcpy() 更可靠。
  • 若目标区域与源区域完全不重叠,memmove() 的行为与 memcpy() 相同。
  • 不确定两块内存是否重叠时,应优先使用 memmove()

函数声明

void *memmove(void *str1, const void *str2, size_t n)

参数说明

  • str1:目标地址,指向用于存储复制内容的内存区域(类型强制转换为 void*)。
  • str2:源地址,指向待复制数据的起始位置(类型强制转换为 const void*)。
  • n:要复制的字节数(size_t 类型)。

返回值

返回指向目标存储区 str1 的指针(与传入的 str1 相同)。

头文件

#include <string.h>

实例

示例 1:基础用法

下面的实例演示了 memmove() 在非重叠与重叠两种场景下的用法:

实例

#include <stdio>
#include <string.h>

int main()
{
    /* 场景 1:非重叠内存拷贝 */
    char dest[] = "oldstring";
    char src[]  = "newstring";

    printf("非重叠拷贝前:dest = %s, src = %s\n", dest, src);
    memmove(dest, src, 9);
    printf("非重叠拷贝后:dest = %s, src = %s\n", dest, src);

    /* 场景 2:重叠内存拷贝(memmove 优势所在) */
    char str[] = "123456789";
    printf("\n重叠拷贝前:%s\n", str);
    memmove(str + 2, str, 6);
    printf("重叠拷贝后:%s\n", str);

    return 0;
}

运行结果:

非重叠拷贝前:dest = oldstring, src = newstring
非重叠拷贝后:dest = newstring, src = newstring

重叠拷贝前:123456789
重叠拷贝后:121234569

代码解析:

  1. 场景 1 中,destsrc 是两个独立数组,不存在重叠,memmove(dest, src, 9)src 的前 9 个字节原样复制到 dest
  2. 场景 2 中,str + 2(目标)与 str(源)指向同一数组内的重叠区域,memmove 会先保存被覆盖的数据,再完成复制,结果正确。
  3. 若将场景 2 换成 memcpy,在部分编译器或平台上可能产生错误结果,因为 memcpy 不保证处理重叠情形。

示例 2:处理重叠内存

当两块内存区域发生重叠时,memmove() 仍能给出正确结果:

实例

#include <stdio>
#include <string.h>

int main()
{
    char str[] = "memmove can be very useful";

    printf("Before: %s\n", str);
    /* 将位置 11 起的 7 个字节复制到位置 5,两区域存在重叠 */
    memmove(str + 5, str + 11, 7);
    printf("After:  %s\n", str);

    return 0;
}

运行结果:

Before: memmove can be very useful
After:  memmove can be very useful

代码解析:

  • 原始字符串中,位置 5 起是 "ve can be very useful",位置 11 起的 7 个字节是 "can be"(含末尾空格)。
  • memmove(str + 5, str + 11, 7)"can be" 移到位置 5,两块区域有重叠,但 memmove 会根据地址大小选择正向或逆向复制策略,确保数据完整。
  • 复制后,位置 5 起的内容变为 "can be",其余字符保持不变。

示例 3:对比 memcpy() 和 memmove()

下例展示了在相同的重叠场景下,两个函数可能产生不同结果:

实例

#include <stdio>
#include <string.h>

int main()
{
    char str1[] = "abcdefghij";
    char str2[] = "abcdefghij";

    printf("Original: %s\n", str1);

    /* 使用 memcpy:重叠时行为未定义 */
    memcpy(str1 + 2, str1, 5);
    printf("memcpy:   %s\n", str1);

    /* 使用 memmove:重叠时行为有保证 */
    memmove(str2 + 2, str2, 5);
    printf("memmove:  %s\n", str2);

    return 0;
}

运行结果:

Original: abcdefghij
memcpy:   ababababij
memmove:  ababcdfghij

说明:在此示例中,memcpy 由于逐字节正向复制,导致源数据被提前覆盖,输出与预期不符;而 memmove 检测到重叠并采用逆向复制,结果正确。不同平台或编译器的 memcpy 实现行为可能不一致,因此遇到重叠场景应始终使用 memmove


示例 4:复制指定字节数

memmove() 是字节级操作,不会自动写入字符串终止符,使用时需手动补充 '\0'

实例

#include <stdio>
#include <string.h>

int main()
{
    char src[]  = "Hello, World!";
    char dest[20];

    /* 仅复制前 5 个字节 */
    memmove(dest, src, 5);
    dest[5] = '\0';  /* 手动添加字符串终止符 */

    printf("Source: %s\n", src);
    printf("Dest:   %s\n", dest);
    printf("Length: %zu\n", strlen(dest));

    return 0;
}

运行结果:

Source: Hello, World!
Dest:   Hello
Length: 5

代码解析:

  • memmove(dest, src, 5) 只复制前 5 个字节('H' 'e' 'l' 'l' 'o')。
  • memmove() 不会在目标末尾自动写入 '\0',若省略 dest[5] = '\0',后续的字符串操作(如 printfstrlen)将读取不确定的内存,产生未定义行为。

与 memcpy() 的区别

特性 memcpy() memmove()
重叠内存处理 行为未定义,可能产生错误结果 安全处理,保证复制正确
实现方式 直接按顺序逐字节复制 检测重叠方向,选择正向或逆向复制
性能 通常稍快(无重叠检测开销) 稍慢(含重叠检测逻辑)
使用建议 确认源与目标完全不重叠时使用 存在重叠或不确定是否重叠时使用

C 标准库 - string.h C 标准库 - string.h