Namespaces
Variants

strncpy, strncpy_s

From zh.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)
分配指定大小的字符串副本
(函数)