命名空间
变体
操作

strncpy, strncpy_s

来自 cppreference.com
< 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 可用。

内容

[编辑] 参数

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

[编辑] 返回值

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

[编辑] 注意

正如 C11 后的 DR 468 所修正的,strncpy_sstrcpy_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++ 文档 for strncpy