命名空间
变体
操作

strncpy, strncpy_s

来自 cppreference.cn
< c‎ | string‎ | byte
定义于头文件 <string.h>
(1)
char *strncpy( char *dest, const char *src, size_t count );
(直到 C99)
char *strncpy( char *restrict dest, const char *restrict src, size_t count );
(C99 起)
errno_t strncpy_s( char *restrict dest, rsize_t destsz,
                   const char *restrict src, rsize_t count );
(2) (C11 起)
1)src 指向的字符数组中复制最多 count 个字符(包括终止空字符,但不包括空字符之后的任何字符)到 dest 指向的字符数组中。
如果在复制完整个 src 数组之前达到 count,则结果字符数组不是以空字符结尾的。
如果从 src 复制终止空字符后,仍未达到 count,则会在 dest 中写入额外的空字符,直到写入的总字符数为 count
如果字符数组重叠,如果 destsrc 不是指向字符数组的指针(包括 destsrc 为空指针),如果 dest 指向的数组大小小于 count,或者如果 src 指向的数组大小小于 count 且不包含空字符,则行为是未定义的。
2)(1) 相同,但函数不再将零写入目标数组以填充到 count,而是在写入终止空字符后停止(如果源中没有空字符,它会在 dest[count] 处写入一个然后停止)。此外,以下错误会在运行时检测到并调用当前安装的 约束处理函数
  • `src` 或 `dest` 是空指针
  • destsz 为零或大于 RSIZE_MAX
  • count 大于 RSIZE_MAX
  • count 大于或等于 destsz,但 destsz 小于或等于 strnlen_s(src, count),换句话说,会发生截断
  • 源字符串和目标字符串之间会发生重叠
如果 dest 指向的字符数组的大小 < strnlen_s(src, destsz) <= destsz,则行为是未定义的;换句话说,错误的 destsz 值不会暴露即将发生的缓冲区溢出。如果 src 指向的字符数组的大小 < strnlen_s(src, count) < destsz,则行为是未定义的;换句话说,错误的 count 值不会暴露即将发生的缓冲区溢出。
与所有边界检查函数一样,只有当实现定义了 __STDC_LIB_EXT1__ 并且用户在包含 <string.h> 之前将 __STDC_WANT_LIB_EXT1__ 定义为整数常量 1 时,才能保证 strncpy_s 可用。

目录

[编辑] 参数

dest - 指向要复制到的字符数组的指针
src - 指向要复制来源的字符数组的指针
count - 要复制的最大字符数
destsz - 目标缓冲区的尺寸

[编辑] 返回值

1) 返回 `dest` 的副本
2) 成功时返回零,错误时返回非零。此外,在错误时,将零写入 dest[0](除非 dest 是空指针或 destsz 为零或大于 RSIZE_MAX),并且可能会用未指定的值覆盖目标数组的其余部分。

[编辑] 注意

根据 C11 后 DR 468 的修正,strncpy_s,与 strcpy_s 不同,只允许在发生错误时覆盖目标数组的其余部分。

strncpy 不同,strncpy_s 不会用零填充目标数组。这在将现有代码转换为边界检查版本时,是一个常见的错误来源。

尽管截断以适应目标缓冲区是一种安全风险,因此对于 strncpy_s 而言是运行时约束违规,但通过将 count 指定为目标数组大小减一,可以获得截断行为:它将复制前 count 个字节并始终附加空终止符:strncpy_s(dst, sizeof dst, src, (sizeof dst)-1);

[编辑] 示例

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
 
int main(void)
{
    char src[] = "hi";
    char dest[6] = "abcdef"; // no null terminator
    strncpy(dest, src, 5); // writes five characters 'h', 'i', '\0', '\0', '\0' to dest
    printf("strncpy(dest, src, 5) to a 6-byte dest gives : ");
    for (size_t n = 0; n < sizeof dest; ++n) {
        char c = dest[n];
        c ? printf("'%c' ", c) : printf("'\\0' ");
    }
 
    printf("\nstrncpy(dest2, src, 2) to a 2-byte dst gives : ");
    char dest2[2];
    strncpy(dest2, src, 2); // truncation: writes two characters 'h', 'i', to dest2
    for (size_t n = 0; n < sizeof dest2; ++n) {
        char c = dest2[n];
        c ? printf("'%c' ", c) : printf("'\\0' ");
    }
    printf("\n");
 
#ifdef __STDC_LIB_EXT1__
    set_constraint_handler_s(ignore_handler_s);
    char dst1[6], src1[100] = "hello";
    errno_t r1 = strncpy_s(dst1, 6, src1, 100);  // writes 0 to r1, 6 characters to dst1
    printf("dst1 = \"%s\", r1 = %d\n", dst1,r1); // 'h','e','l','l','o','\0' to dst1
 
    char dst2[5], src2[7] = {'g','o','o','d','b','y','e'};
    errno_t r2 = strncpy_s(dst2, 5, src2, 7);    // copy overflows the destination array
    printf("dst2 = \"%s\", r2 = %d\n", dst2,r2); // writes nonzero to r2,'\0' to dst2[0]
 
    char dst3[5];
    errno_t r3 = strncpy_s(dst3, 5, src2, 4);    // writes 0 to r3, 5 characters to dst3
    printf("dst3 = \"%s\", r3 = %d\n", dst3,r3); // 'g', 'o', 'o', 'd', '\0' to dst3
#endif
}

可能的输出

strncpy(dest, src, 5) to a 6-byte dst gives : 'h' 'i' '\0' '\0' '\0' 'f'
strncpy(dest2, src, 2) to a 2-byte dst gives : 'h' 'i'
dst1 = "hello", r1 = 0
dst2 = "", r2 = 22
dst3 = "good", r3 = 0

[编辑] 参考

  • C17 标准 (ISO/IEC 9899:2018)
  • 7.24.2.4 strncpy 函数 (p: 265)
  • K.3.7.1.4 strncpy_s 函数 (p: 447-448)
  • C11 标准 (ISO/IEC 9899:2011)
  • 7.24.2.4 strncpy 函数 (p: 363-364)
  • K.3.7.1.4 strncpy_s 函数 (p: 616-617)
  • C99 标准 (ISO/IEC 9899:1999)
  • 7.21.2.4 strncpy 函数 (p: 326-327)
  • C89/C90 标准 (ISO/IEC 9899:1990)
  • 4.11.2.4 strncpy 函数

[编辑] 另请参阅

将一个字符串复制到另一个字符串
(函数) [编辑]
将一个缓冲区复制到另一个缓冲区
(函数) [编辑]
(动态内存 TR)
分配一个指定大小的字符串副本
(函数) [编辑]
C++ 文档 适用于 strncpy