ZetCode

C memcpy 函数

最后修改:2025 年 4 月 8 日

内存操作是 C 编程的基础,而 memcpy 是在内存位置之间复制数据的关键函数。本教程将深入探讨 memcpy,包括其语法、用法和潜在陷阱。我们将探索实际示例,并为关键应用程序讨论更安全的替代方案。了解 memcpy 有助于优化内存操作,同时保持程序的安全性和可靠性。

什么是 memcpy?

memcpy 函数将一块内存从一个位置复制到另一个位置。它在 string.h 中声明,并接受三个参数:目标指针、源指针和要复制的字节数。memcpy 执行二进制复制,不检查重叠区域或缓冲区大小。对于安全关键代码,请考虑使用 memmove 处理重叠区域,或使用 memcpy_s 进行边界检查的复制。

基本 memcpy 用法

此示例演示了使用 memcpy 在两个数组之间复制数据。

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

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

    // Copy 14 bytes (including null terminator)
    memcpy(dest, src, 14);

    printf("Source: %s\n", src);
    printf("Destination: %s\n", dest);

    return 0;
}

在此,memcpy 将 14 个字节从 src 复制到 dest,包括空终止符。目标缓冲区必须足够大以容纳复制的数据。当您知道确切所需大小时,这是一种简单高效的数据复制方式。始终确保目标有足够的空间以防止缓冲区溢出。

使用 memcpy 复制结构

memcpy 可以高效地复制整个结构,如本示例所示。

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

typedef struct {
    int id;
    char name[20];
    float score;
} Student;

int main() {
    Student s1 = {101, "Alice", 95.5};
    Student s2;

    // Copy the entire structure
    memcpy(&s2, &s1, sizeof(Student));

    printf("Copied Student:\n");
    printf("ID: %d\n", s2.id);
    printf("Name: %s\n", s2.name);
    printf("Score: %.1f\n", s2.score);

    return 0;
}

此示例使用 memcpy 复制一个 Student 结构。sizeof 运算符确保我们复制确切所需的字节数。对于大型结构,此方法比逐个字段赋值更快。请注意,这适用于不包含指向动态分配内存的指针的简单结构。

处理重叠内存区域

此示例演示了在存在重叠区域时使用 memcpy 的危险。

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

int main() {
    char data[] = "ABCDEFGHIJ";
    
    // Attempt to copy with overlapping regions
    memcpy(data + 2, data, 5);

    printf("Result: %s\n", data);

    return 0;
}

此代码显示了未定义的行为,因为 memcpy 不处理重叠的内存区域。源和目标重叠了 3 个字节。对于这种情况,应改用 memmove,因为它能正确处理重叠。此程序的输出是不可预测的,并且在编译器和平台之间可能有所不同。

安全替代方案:memcpy_s

此示例演示了 C11 中提供的更安全的 memcpy_s 函数。

safe_copy.c
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>

int main() {
    char src[10] = "Test";
    char dest[5];
    
    // Safe copy with bounds checking
    errno_t result = memcpy_s(dest, sizeof(dest), src, sizeof(src));

    if (result != 0) {
        printf("Error: Buffer too small or invalid parameters\n");
        return 1;
    }

    printf("Copied safely: %s\n", dest);
    return 0;
}

memcpy_s 添加了边界检查,如果目标太小则返回错误。这有助于防止缓冲区溢出。函数成功时返回零,失败时返回非零。虽然它不是普遍可用的,但当以 C11 或更高标准为目标并支持边界检查时,建议在安全关键代码中使用它。

__STDC_WANT_LIB_EXT1__ 被定义为 1,明确表示程序希望使用 C11 标准库的可选扩展。如果没有此宏,编译器可能不会提供某些更安全的功能,包括 memcpy_s。此功能允许开发人员选择性地启用附加功能,以提高其程序的安全性和可靠性。

复制部分数组

此示例展示了如何使用 memcpy 复制数组的一部分。

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

int main() {
    int src[] = {1, 2, 3, 4, 5, 6, 7, 8};
    int dest[4];
    
    // Copy middle 4 elements (3,4,5,6)
    memcpy(dest, src + 2, 4 * sizeof(int));

    printf("Copied elements: ");
    for (int i = 0; i < 4; i++) {
        printf("%d ", dest[i]);
    }
    printf("\n");

    return 0;
}

在这里,memcpy 从源数组的第三个元素开始复制四个整数。大小计算为 4 * sizeof(int) 以获取正确的字节数。此技术对于提取数组或缓冲区的一部分很有用。始终验证目标是否有足够的空间来存储复制的数据。

使用 memcpy 的最佳实践

来源

C memcpy 文档

本教程探讨了 memcpy 函数,从基本用法到高级注意事项。虽然它功能强大,但请始终小心使用内存操作,以防止程序中出现安全漏洞和未定义行为。

作者

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

列表 C 标准库