Namespaces
Variants

strncpy, strncpy_s

From cppreference.net
< 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
如果字符数组重叠,如果 dest src 不是指向字符数组的指针(包括 dest src 是空指针的情况),如果 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标准缺陷报告468的修正,与 strcpy_s 不同, strncpy_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"; // 无空终止符
    strncpy(dest, src, 5); // 向dest写入五个字符 'h', 'i', '\0', '\0', '\0'
    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); // 截断:向dest2写入两个字符 'h', 'i'
    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);  // 向r1写入0,向dst1写入6个字符
    printf("dst1 = \"%s\", r1 = %d\n", dst1,r1); // 向dst1写入 'h','e','l','l','o','\0'
    char dst2[5], src2[7] = {'g','o','o','d','b','y','e'};
    errno_t r2 = strncpy_s(dst2, 5, src2, 7);    // 复制操作溢出目标数组
    printf("dst2 = \"%s\", r2 = %d\n", dst2,r2); // 向r2写入非零值,向dst2[0]写入'\0'
    char dst3[5];
    errno_t r3 = strncpy_s(dst3, 5, src2, 4);    // 向r3写入0,向dst3写入5个字符
    printf("dst3 = \"%s\", r3 = %d\n", dst3,r3); // 向dst3写入 'g', 'o', 'o', 'd', '\0'
#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 函数 (页码: 363-364)
  • K.3.7.1.4 strncpy_s 函数 (页码: 616-617)
  • C99标准(ISO/IEC 9899:1999):
  • 7.21.2.4 strncpy函数(页码:326-327)
  • C89/C90 标准 (ISO/IEC 9899:1990):
  • 4.11.2.4 strncpy 函数

参见

复制一个字符串到另一个字符串
(函数)
复制一个缓冲区到另一个缓冲区
(函数)
(dynamic memory TR)
分配指定大小的字符串副本
(函数)