ZetCode

C strncpy_s 函数

最后修改:2025 年 4 月 8 日

字符串操作在 C 编程中是基础性的,而 strncpy_s 是安全字符串复制的关键函数。本教程将深入介绍 strncpy_s,包括其语法、用法以及相对于不安全替代品的优势。我们将探讨实际示例,并讨论何时使用此更安全的函数。理解 strncpy_s 有助于防止缓冲区溢出和其他常见的字符串相关漏洞。

什么是 strncpy_s?

strncpy_s 函数是 strncpy 的边界检查版本,它将最多指定数量的字符从源复制到目标。它是 C11 的 Annex K 边界检查接口的一部分,并提供运行时约束检查。与 strcpystrncpy 不同,如果目标缓冲区太小,它会返回错误。这使得它对于必须防止缓冲区溢出的安全关键应用程序更安全。

基本的 strncpy_s 用法

此示例演示了使用 strncpy_s 进行基本的字符串复制。

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

int main() {
    char src[] = "Hello, World!";
    char dest[20];
    
    // Safe copy with bounds checking
    errno_t result = strncpy_s(dest, sizeof(dest), src, sizeof(src));

    if (result != 0) {
        printf("Error: %d\n", result);
        return 1;
    }

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

    return 0;
}

在此,strncpy_s 在进行边界检查的情况下将 src 的内容复制到 dest。该函数检查 dest 是否有足够的空间。如果成功,它返回 0;否则,它返回一个非零错误码。这可以防止使用 strcpystrncpy 时可能发生的缓冲区溢出。始终检查返回值以处理错误条件。

处理小型目标缓冲区

此示例显示了 strncpy_s 如何处理目标缓冲区太小的情况。

small_buffer.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];
    
    // Attempt to copy with insufficient space
    errno_t result = strncpy_s(dest, sizeof(dest), src, sizeof(src));

    if (result != 0) {
        printf("Error detected: %d\n", result);
        printf("Destination buffer is too small\n");
        return 1;
    }

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

此代码演示了 strncpy_s 的安全功能。目标缓冲区对于源字符串来说太小,因此函数返回一个错误码。在错误发生时,目标缓冲区保持不变,从而防止了潜在的缓冲区溢出。这种行为与 strncpy 不同,后者会在没有警告的情况下截断。始终妥善处理错误条件。

部分字符串复制

此示例显示了如何使用 strncpy_s 只复制字符串的一部分。

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

int main() {
    char src[] = "Programming in C";
    char dest[20];
    
    // Copy only first 11 characters
    errno_t result = strncpy_s(dest, sizeof(dest), src, 11);

    if (result != 0) {
        printf("Error: %d\n", result);
        return 1;
    }

    printf("Partial copy: %s\n", dest);
    return 0;
}

在这里,strncpy_s 只从源字符串复制前 11 个字符。该函数确保目标缓冲区足够大以容纳指定的字符数。与 strncpy 不同,它保证了目标字符串的空终止。这使得结果始终是一个有效的字符串。count 参数指定了要复制的最大字符数。

使用动态分配进行复制

此示例演示了将 strncpy_s 与动态分配的内存一起使用。

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

int main() {
    char src[] = "Dynamic memory example";
    size_t len = strlen(src) + 1;
    char *dest = malloc(len);
    
    if (dest == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    // Safe copy to dynamically allocated buffer
    errno_t result = strncpy_s(dest, len, src, len);

    if (result != 0) {
        printf("Error: %d\n", result);
        free(dest);
        return 1;
    }

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

此示例展示了带有动态分配缓冲区的 strncpy_s。缓冲区大小的计算足以精确容纳源字符串(包括空终止符)。该函数验证复制操作是否在边界内。始终在完成动态分配的内存后释放它。当处理编译时长度未知的字符串时,此模式很有用。

零长度复制和边缘情况

此示例探讨了 strncpy_s 的边缘情况,包括零长度复制。

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

int main() {
    char src[] = "Edge case testing";
    char dest[20] = {0};
    
    // Case 1: Zero-length copy
    errno_t result = strncpy_s(dest, sizeof(dest), src, 0);
    printf("Zero-length result: %d\n", result);
    
    // Case 2: Destination size zero
    result = strncpy_s(dest, 0, src, sizeof(src));
    printf("Zero-dest-size result: %d\n", result);
    
    // Case 3: NULL pointers
    result = strncpy_s(NULL, sizeof(dest), src, sizeof(src));
    printf("NULL dest result: %d\n", result);
    
    return 0;
}

此代码测试了 strncpy_s 的各种边缘情况。对于零长度目标或 NULL 指针等无效操作,该函数会返回非零值。这些检查有助于及早发现编程错误。处理字符串操作时,始终测试边缘情况。与传统字符串函数不同,这些情况下的行为定义明确。

使用 strncpy_s 的最佳实践

来源

C strncpy_s 文档

本教程从基本用法到高级注意事项,全面探讨了 strncpy_s 函数。虽然比传统的字符串函数更冗长,但其安全功能对于安全编程至关重要。在处理不受信任的输入或安全关键代码时,始终优先使用边界检查函数。

作者

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

列表 C 标准库