在以下关于strncpy
:http ://www.cplusplus.com/reference/clibrary/cstring/strncpy/ ,它提到以下内容:
没有空字符隐式附加到目标的末尾,因此只有当源中 C 字符串的长度小于 num 时,目标才会以空字符结尾。
这句话是什么意思?
这意味着,例如,如果您的源字符串是 20 个字符加上一个空终止符,并且您strncpy
指定的字符少于 21 个,则目标字符串不会附加空值。
这是因为它的工作方式:strncpy
保证它将准确写入 N 个字节,其中 N 是传入的长度值。
如果源字符串(无空字节)的长度小于该长度,它将用空值填充目标区域。如果它等于或大于,则不会将 null 添加到目标。
这意味着从技术上讲,它可能不是您得到的 C 字符串。这可以通过以下代码解决:
char d[11]; // Have enough room for string and null.
strncpy (d, s, 10); // Copy up to 10 bytes of string, null the rest.
d[10] = '\0'; // Then append null manually in case s was too long.
您分配 11 个字节(数组索引 0..10),最多复制 10 个(索引 0..9),然后将第 11 个(索引 10)设置为空。
下图显示了将各种大小的字符串写入 10 个字符的区域的三种可能性,strncpy (d, s, 10)
其中 where.
表示空字节:
s d
------------- ----------
Hello. Hello.....
Hello Fred. Hello Fred
Hello George. Hello Geor
请注意,在第二种和第三种情况下,没有写入空字节,因此,如果您将d
其视为字符串,您可能会对结果感到失望。
该字符串"foo"
有 3 个字符 + 1 个空终止符(它存储为"foo\0"
),总长度为 4。如果您strncpy
使用n=3
(或更少)调用,它不会将空终止符附加到目标字符串的末尾,但会复制只有"foo"
. 由于缺少指示字符串结束的空终止符,尝试打印结果字符串将导致未定义的行为。
您必须非常小心这一点,要么传递n
大于最大源的 1,要么自己添加空终止符。
这意味着它会复制源字符串的终止空值,但如果源字符串不适合目标,则不会添加终止空值。
在存储为char
's 数组的 C 字符串中,它们以空字符结尾,这意味着它们0
在末尾附加了一个额外的字符,这标志着字符串的结尾,以后可以用来计算字符串的长度。所以字符串"hello"
在内存中看起来像这样:
char hello[] = {'h', 'e', 'l', 'l', 'o', 0};
通常,当您复制一个字符串时,该null
字符也应该被复制。所以字符串缓冲区所需的内存是它的长度+1(例如(strlen(hello) + 1) * sizeof(char)
)。
函数strncpy
允许您只复制尽可能多的字符以适合提供的缓冲区。如果您提供的缓冲区不够大,无法容纳额外的null
,则不会添加。或者如果字符串被剪切,它不会以空值结尾。
char hello[] = "hello"; // 5 characters, 6 bytes long
char hel[3];
strncpy(hel, hello, 3); // hel is {'h', 'e', 'l'}
调用 后应始终小心strncpy
,因为结果可能不是有效的 C 字符串。如果字符串不是以空值结尾的,就不可能知道它的长度,并且大多数字符串操作函数都会失败或会做一些意想不到的事情。
这意味着只有num
字节会从源缓冲区复制到目标缓冲区;所以如果源字符串的长度大于num
等于
建议使用strlcpy代替。
的语义strncpy()
,即使在上面的 C++ 参考中得到了准确的解释,也被广泛误解。此函数的行为违反直觉且容易出错。
为了避免在使用它或进一步开发过程中出现问题,当维护者误读代码并添加更多细微错误时,有一个简单的解决方案:永远不要使用此功能。
您可以在Bruce Dawson 的这篇文章中阅读有关此内容的更多详细信息。
回答您的问题:如果源字符串长于作为第三个参数传递的大小(通常对应于目标缓冲区的大小),则该函数会将 size 个字符复制到目标并且其中不存在空字节。然后调用strlen(destination);
将调用未定义的行为,因为它将尝试读取数组末尾之外的内容,直到找到空终止符。这种特定的行为是strncpy
容易出错的原因。