C memmove 函数
最后修改:2025 年 4 月 8 日
内存操作在 C 编程中是基础性的,而 memmove 是一个用于在内存位置之间安全复制数据的函数。本教程将深入介绍 memmove,包括其语法、用法以及相对于 memcpy 的优势。我们将通过实际示例来展示它如何安全地处理重叠的内存区域。理解 memmove 有助于编写健壮的程序,避免内存操作中的未定义行为。
什么是 memmove?
memmove 函数安全地将一个内存块从一个位置复制到另一个位置。它在 string.h 中声明,并接受三个参数:目标指针、源指针以及要复制的字节数。memmove 通过在复制前检查内存范围来正确处理重叠的内存区域。与 memcpy 不同,即使源缓冲区和目标缓冲区重叠,它也能保证正确的行为。
memmove 的基本用法
本示例演示了如何使用 memmove 在两个数组之间复制数据。
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest[20];
// Copy 14 bytes (including null terminator)
memmove(dest, src, 14);
printf("Source: %s\n", src);
printf("Destination: %s\n", dest);
return 0;
}
在此,memmove 将 14 个字节从 src 复制到 dest,包括空终止符。当缓冲区不重叠时,该函数的作用类似于 memcpy。目标缓冲区必须足够大才能容纳复制的数据。这是在不确定潜在重叠的情况下安全复制内存的方法。
处理重叠内存区域
本示例演示了 memmove 正确处理重叠区域的能力。
#include <stdio.h>
#include <string.h>
int main() {
char data[] = "ABCDEFGHIJ";
// Move data within the same buffer (overlapping)
memmove(data + 2, data, 5);
printf("Result: %s\n", data);
return 0;
}
这段代码可以安全地复制数据,即使源和目标重叠。memmove 会检查内存范围并以正确的方向复制,以保持数据完整性。输出将是“ABABCDEFIJ”,因为前 5 个字节是从位置 2 开始复制的。这种行为是有保证的,并且在不同平台之间是可移植的。
移动数组元素
本示例展示了如何使用 memmove 在数组内移动元素。
#include <stdio.h>
#include <string.h>
int main() {
int nums[] = {1, 2, 3, 4, 5, 6, 7, 8};
size_t count = sizeof(nums)/sizeof(nums[0]);
// Shift elements left by 2 positions
memmove(nums, nums + 2, (count - 2) * sizeof(int));
printf("Shifted array: ");
for (size_t i = 0; i < count - 2; i++) {
printf("%d ", nums[i]);
}
printf("\n");
return 0;
}
在此,memmove 将数组元素向左移动两个位置。该函数能正确处理重叠的源和目标区域。大小计算使用了 (count - 2) * sizeof(int) 来获取正确的字节数。移动后,数组的前六个位置包含 {3,4,5,6,7,8}。
使用 memmove 复制结构体
memmove 可以安全地复制结构体,即使它们可能重叠。
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[20];
float score;
} Student;
int main() {
Student students[3] = {
{101, "Alice", 95.5},
{102, "Bob", 88.0},
{103, "Charlie", 91.2}
};
// Move second student to first position
memmove(&students[0], &students[1], sizeof(Student));
printf("First student is now: %s\n", students[0].name);
return 0;
}
本示例使用 memmove 在数组内移动一个 Student 结构体。sizeof(Student) 确保我们复制了所需的精确字节数。由于源和目标在同一个数组中,因此 memmove 是正确的选择。执行后,第一个数组元素包含 Bob 的数据。
实现循环缓冲区
本示例演示了如何在循环缓冲区实现中使用 memmove。
#include <stdio.h>
#include <string.h>
#define BUF_SIZE 16
void add_to_buffer(char *buf, size_t *count, char data) {
if (*count == BUF_SIZE) {
// Make room by shifting left
memmove(buf, buf + 1, BUF_SIZE - 1);
(*count)--;
}
buf[(*count)++] = data;
}
int main() {
char buffer[BUF_SIZE] = {0};
size_t count = 0;
// Fill the buffer
for (char c = 'A'; c < 'Q'; c++) {
add_to_buffer(buffer, &count, c);
}
// Add one more, causing a shift
add_to_buffer(buffer, &count, 'Q');
printf("Buffer: %.*s\n", (int)count, buffer);
return 0;
}
这段代码使用 memmove 在缓冲区已满时移动元素,从而实现了一个简单的循环缓冲区。该函数维护最近的 BUF_SIZE 个字符。当添加 'Q' 时,它会将所有字符向左移动以腾出空间。输出显示“BCDEFGHIJKLMNOPQ”——第一个 'A' 被丢弃以保持缓冲区大小。
使用 memmove 的最佳实践
- 优先于 memcpy: 不确定内存重叠时,请使用
memmove。 - 检查缓冲区大小: 确保目标有足够的空间容纳移动的数据。
- 使用正确的尺寸: 使用
sizeof仔细计算字节数。 - 验证指针: 确保源指针和目标指针都有效。
- 考虑性能:
memmove可能比memcpy稍慢。
来源
本教程从基本用法到高级内存操作,全面介绍了 memmove 函数。它处理重叠区域的能力使其比 memcpy 在许多用例中更安全。在 C 中处理低级操作时,请始终考虑内存安全。
作者
列表 C 标准库。