ZetCode

C memcpy_s 函数

最后修改:2025 年 4 月 8 日

内存安全在 C 编程中至关重要,而 memcpy_s 提供了比传统 memcpy 更安全的选择。本教程将深入介绍 memcpy_s,包括其语法、用法和优点。我们将通过实际示例演示带有边界检查的安全内存复制。理解 memcpy_s 有助于防止缓冲区溢出和其他内存相关漏洞,尤其是在关键应用程序中。

什么是 memcpy_s?

memcpy_s 函数是 C11 中引入的 memcpy 的一个带有边界检查的版本。它在缓冲区之间复制内存,同时验证目标缓冲区的大小。如果目标缓冲区太小或任何参数无效,该函数将返回一个错误。与 memcpy 不同,memcpy_s 有助于防止缓冲区溢出漏洞。它是 C11 附录 K 边界检查接口的一部分。

为什么使用 memcpy_s 而不是 memcpy?

memcpy_s 提供了 memcpy 所不具备的运行时检查。它会验证目标缓冲区大小是否与请求的复制操作匹配。这可以防止可能导致安全漏洞的缓冲区溢出。虽然并非普遍可用,但建议用于安全性关键的代码。该函数返回错误代码,有助于诊断内存安全问题。

基本的 memcpy_s 用法

本示例演示了带有边界检查的 memcpy_s 的基本用法。

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

int main() {
    char src[] = "Safe copying";
    char dest[20];
    
    errno_t result = memcpy_s(dest, sizeof(dest), src, sizeof(src));

    if (result == 0) {
        printf("Copied successfully: %s\n", dest);
    } else {
        printf("Error %d: Copy failed\n", result);
    }

    return 0;
}

此代码使用 memcpy_s 安全地复制字符串。在复制之前,会检查目标缓冲区大小是否与源大小匹配。函数成功时返回零,失败时返回非零。即使复制失败,错误处理也能确保程序的稳定性。这种方法可以防止未经验证的 memcpy 用法中存在的缓冲区溢出漏洞。

处理复制失败

此示例显示了因目标空间不足导致 memcpy_s 失败时的正确错误处理。

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

int main() {
    char src[] = "This string is too long";
    char dest[10];
    
    errno_t result = memcpy_s(dest, sizeof(dest), src, sizeof(src));

    if (result != 0) {
        printf("Error %d: ", result);
        if (result == ERANGE) {
            printf("Destination buffer too small\n");
        } else if (result == EINVAL) {
            printf("Invalid parameters\n");
        }
        return 1;
    }

    printf("Copied successfully\n");
    return 0;
}

此代码演示了如何处理 memcpy_s 的不同错误条件。目标缓冲区故意设置得太小。当目标缓冲区不足时,函数返回 ERANGE。其他可能的错误包括用于无效参数的 EINVAL。正确的错误处理使程序更加健壮和安全。

安全地复制结构体

此示例展示了如何使用 memcpy_s 安全地复制结构体。

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

typedef struct {
    int id;
    char name[30];
    float balance;
} Account;

int main() {
    Account acc1 = {1001, "John Doe", 1250.75f};
    Account acc2;
    
    errno_t result = memcpy_s(&acc2, sizeof(Account), 
                            &acc1, sizeof(Account));

    if (result == 0) {
        printf("Account copied successfully\n");
        printf("ID: %d, Name: %s, Balance: %.2f\n", 
               acc2.id, acc2.name, acc2.balance);
    } else {
        printf("Error copying account structure\n");
    }

    return 0;
}

此代码使用 memcpy_s 安全地复制整个 Account 结构体。在复制之前会验证目标缓冲区大小。该函数可确保在结构体复制过程中不会发生缓冲区溢出。这种技术对于复杂的数据结构特别有用。

部分缓冲区复制

此示例演示了使用 memcpy_s 进行安全的部分缓冲区复制。

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

int main() {
    int src[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int dest[5];
    
    // Copy first 5 elements (20 bytes)
    errno_t result = memcpy_s(dest, sizeof(dest), 
                           src, 5 * sizeof(int));

    if (result == 0) {
        printf("Copied elements: ");
        for (int i = 0; i < 5; i++) {
            printf("%d ", dest[i]);
        }
        printf("\n");
    } else {
        printf("Error copying partial array\n");
    }

    return 0;
}

此代码使用 memcpy_s 安全地复制整数数组的一部分。目标缓冲区大小被正确计算和验证。该函数可确保仅复制指定数量的字节。当处理数组切片时,此方法可以防止缓冲区溢出。

零长度复制保护

此示例显示了 memcpy_s 如何处理零长度复制请求。

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

int main() {
    char src[] = "Test data";
    char dest[20];
    
    // Attempt zero-length copy
    errno_t result = memcpy_s(dest, sizeof(dest), 
                           src, 0);

    if (result == 0) {
        printf("Zero-length copy succeeded\n");
    } else {
        printf("Error %d: Zero-length copy failed\n", result);
    }

    return 0;
}

此代码测试了 memcpy_s 在零长度复制下的行为。该函数能够正确处理此边缘情况而不会出错。零长度复制是有效的操作,不会对内存产生任何更改。memcpy_s 在此情况下返回成功,同时仍然验证缓冲区的有效性。

使用 memcpy_s 的最佳实践

来源

C memcpy_s 文档

本教程探讨了 memcpy_s 函数,展示了它相对于传统 memcpy 的优势。虽然需要稍多一些代码,但它为安全编程提供了必要的内存安全保证。在安全性关键的应用程序中,始终考虑使用带边界检查的函数。

作者

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

列表 C 标准库