命名空间
变体
操作

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] 处写入一个空字符然后停止)。此外,以下错误在运行时被检测到,并调用当前安装的 约束处理函数
  • srcdest 是空指针
  • 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 可用。

目录

[edit] 参数

dest - 指向要复制到的字符数组的指针
src - 指向要从中复制的字符数组的指针
count - 要复制的最大字符数
destsz - 目标缓冲区的大小

[edit] 返回值

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

[edit] 注解

正如 C11 之后 DR 468 修正的那样,与 strcpy_s 不同,strncpy_s 仅在发生错误时才允许覆盖目标数组的剩余部分。

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

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

[edit] 示例

#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

[edit] 参考文献

  • 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 函数

[edit] 参见

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