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 个字符。
若字符数组重叠,若 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 值不暴露行将发生的缓冲区溢出。
同所有边界检查函数,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,仅若错误发生才被允许破坏目标数组的剩余部分。

不同于 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); // 写入五个字符 '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)
分配字符串副本,至多到指定的大小
(函数)