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 值不暴露行将发生的缓冲区溢出。
同所有边界检查函数,strncpy_s,仅若实现定义 __STDC_LIB_EXT1__ 且用户在包含 <string.h> 前定义 __STDC_WANT_LIB_EXT1__ 为整数常量 1 才保证可用。

参数

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

返回值

1) 返回 dest 的副本
2) 成功时返回零,错误时返回非零。而且,在错误时写入零到 dest[0](除非 dest 为空指针,或 destsz 为零或大于 RSIZE_MAX),而且可能以未指定值破坏目标数组的剩余部分。

注解

按 C11 后的 DR 468 更正,strncpy_s 不同于 strcpy_s,仅若错误发生才被允许破坏目标数组的剩余部分。

不同于 strncpystrncpy_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); // 写入五个字符 'h', 'i', '\0', '\0', '\0' 到 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); // 截断:写入二个字符 'h', 'i', 到 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";
    int r1 = strncpy_s(dst1, 6, src1, 100);      // 写入 0 到 r1,6 个字符到 dst1
    printf("dst1 = \"%s\", r1 = %d\n", dst1,r1); // 'h','e','l','l','o','\0' 到 dst1
 
    char dst2[5], src2[7] = {'g','o','o','d','b','y','e'};
    int r2 = strncpy_s(dst2, 5, src2, 7);        // 复制溢出目标数组
    printf("dst2 = \"%s\", r2 = %d\n", dst2,r2); // 写入非零到 r2,'\0' 到 dst2[0]
 
    char dst3[5];
    int r3 = strncpy_s(dst3, 5, src2, 4);        // 写入 0 到 r3,5 个字符到 dst3
    printf("dst3 = \"%s\", r3 = %d\n", dst3,r3); // 'g', 'o', 'o', 'd', '\0' 到 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 The strncpy function (第 265 页)
  • K.3.7.1.4 The strncpy_s function (第 447-448 页)
  • C11 标准(ISO/IEC 9899:2011):
  • 7.24.2.4 The strncpy function (第 363-364 页)
  • K.3.7.1.4 The strncpy_s function (第 616-617 页)
  • C99 标准(ISO/IEC 9899:1999):
  • 7.21.2.4 The strncpy function (第 326-327 页)
  • C89/C90 标准(ISO/IEC 9899:1990):
  • 4.11.2.4 The strncpy function

参阅

复制一个字符串给另一个
(函数)
将一个缓冲区复制到另一个
(函数)
(动态内存 TR)
分配字符串副本,至多到指定的大小
(函数)