ZetCode

C memmove 函数

最后修改:2025 年 4 月 8 日

内存操作在 C 编程中是基础性的,而 memmove 是一个用于在内存位置之间安全复制数据的函数。本教程将深入介绍 memmove,包括其语法、用法以及相对于 memcpy 的优势。我们将通过实际示例来展示它如何安全地处理重叠的内存区域。理解 memmove 有助于编写健壮的程序,避免内存操作中的未定义行为。

什么是 memmove?

memmove 函数安全地将一个内存块从一个位置复制到另一个位置。它在 string.h 中声明,并接受三个参数:目标指针、源指针以及要复制的字节数。memmove 通过在复制前检查内存范围来正确处理重叠的内存区域。与 memcpy 不同,即使源缓冲区和目标缓冲区重叠,它也能保证正确的行为。

memmove 的基本用法

本示例演示了如何使用 memmove 在两个数组之间复制数据。

basic_move.c
#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 正确处理重叠区域的能力。

overlap_move.c
#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 在数组内移动元素。

array_shift.c
#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 可以安全地复制结构体,即使它们可能重叠。

struct_move.c
#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

circular_buffer.c
#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 的最佳实践

来源

C memmove 文档

本教程从基本用法到高级内存操作,全面介绍了 memmove 函数。它处理重叠区域的能力使其比 memcpy 在许多用例中更安全。在 C 中处理低级操作时,请始终考虑内存安全。

作者

我叫 Jan Bodnar,是一名敬业的程序员,对编码充满热情。自 2007 年以来,我通过 1400 多篇文章和 8 本电子书分享我的专业知识。凭借十多年的教学经验,我致力于让编程变得易于理解且引人入胜。

列表 C 标准库