C 库函数 - memmove()
memmove() 是 C 语言标准库 <string.h> 中的内存操作函数,用于将指定数量的字节从源地址复制到目标地址,并能安全处理内存重叠的情形。
单词释义:memmove 是 memory 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;
}
#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 中,
dest和src是两个独立数组,不存在重叠,memmove(dest, src, 9)将src的前 9 个字节原样复制到dest。 - 场景 2 中,
str + 2(目标)与str(源)指向同一数组内的重叠区域,memmove会先保存被覆盖的数据,再完成复制,结果正确。 - 若将场景 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;
}
#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;
}
#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;
}
#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',后续的字符串操作(如printf、strlen)将读取不确定的内存,产生未定义行为。
与 memcpy() 的区别
| 特性 | memcpy() | memmove() |
|---|---|---|
| 重叠内存处理 | 行为未定义,可能产生错误结果 | 安全处理,保证复制正确 |
| 实现方式 | 直接按顺序逐字节复制 | 检测重叠方向,选择正向或逆向复制 |
| 性能 | 通常稍快(无重叠检测开销) | 稍慢(含重叠检测逻辑) |
| 使用建议 | 确认源与目标完全不重叠时使用 | 存在重叠或不确定是否重叠时使用 |

C 标准库 - string.h