strncpy, strncpy_s
|
定义于头文件
<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起) |
src
所指向的字符数组中的最多
count
个字符(包括终止空字符,但不包括空字符之后的任何字符)复制到
dest
所指向的字符数组中。
src
之前达到
count
,则生成的字符数组不会以空字符结尾。
src
复制终止空字符后仍未达到
count
,则继续向
dest
写入额外的空字符,直至写入字符总数达到
count
。
dest
或
src
不是指向字符数组的指针(包括
dest
或
src
是空指针的情况),如果
dest
指向的数组大小小于
count
,或者如果
src
指向的数组大小小于
count
且其中不包含空字符,则行为未定义。
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 | - | 目标缓冲区的大小 |
返回值
dest
的副本
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 函数
参见
|
(C11)
|
复制一个字符串到另一个字符串
(函数) |
|
(C11)
|
复制一个缓冲区到另一个缓冲区
(函数) |
|
(dynamic memory TR)
|
分配指定大小的字符串副本
(函数) |
|
C++ documentation
for
strncpy
|
|