关于c ++:strncpy文档问题

strncpy documentation question

关于strncpy的以下内容:http://www.cplusplus.com/reference/clibrary/cstring/strncpy/,其中提到了以下内容:

No null-character is implicitly appended to the end of destination, so destination will only be null-terminated if the length of the C string in source is less than num.

这句话是什么意思?


这意味着,例如,如果您的源字符串为20个字符加上一个空终止符,并且您的strncpy指定少于21个字符,则目标字符串将不会附加空值。

这是因为它的工作方式:strncpy保证它将精确地写入N个字节,其中N是传入的长度值。

如果源字符串的长度(无null字节)小于该长度,它将用null填充目标区域。如果等于或大于,则不会为目标添加空值。

这意味着从技术上讲它可能不是您得到的C字符串。可以使用以下代码解决:

1
2
3
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)设置为null。

下图显示了使用strncpy (d, s, 10)将各种大小的字符串写入10个字符的区域的三种可能性,其中.代表空字节:

1
2
3
4
5
s              d
-------------  ----------
Hello.         Hello.....
Hello Fred.    Hello Fred
Hello George.  Hello Geor

请注意,在第二种和第三种情况下,不会写入任何空字节,因此,如果将d视为字符串,则结果可能会令人失望。


字符串"foo"具有3个字符+ 1个空终止符(存储为"foo\0"),总长度为4。如果用n=3(或更少)调用strncpy,则不会附加空-终止符到目标字符串的末尾,但仅复制"foo"。由于缺少指示字符串结尾的空终止符,因此尝试打印结果字符串将导致不确定的行为。

您必须非常小心,并通过n比最大源大一个,或者自己添加null终止符。


在以char数组存储的C字符串中,它们以空值结尾,这意味着它们在末尾附加了一个额外的0,这标志着字符串的结尾,以后可以用来找出字符串的长度。因此字符串"hello"在内存中看起来像这样:

1
char hello[] = {'h', 'e', 'l', 'l', 'o', 0};

通常,复制字符串时,还应复制null字符。因此,字符串缓冲区所需的内存是其长度+ 1(例如(strlen(hello) + 1) * sizeof(char))。

函数strncpy允许您仅复制尽可能多的字符,以适合提供的缓冲区。如果您提供的缓冲区不足以容纳额外的null,则不会添加它。或者,如果字符串被剪切,则不会以null终止。

1
2
3
char hello[] ="hello"; // 5 characters, 6 bytes long
char hel[3];
strncpy(hel, hello, 3); // hel is {'h', 'e', 'l'}

调用strncpy后,应始终小心,因为结果可能不是有效的C字符串。如果字符串不是以Null结尾的字符串,则不可能知道其长度,并且大多数字符串操作函数都会失败或发生意外情况。


这意味着它将复制源字符串的终止null,但如果源字符串不适合目标,则不添加终止null。


这意味着只有num个字节将从源缓冲区复制到目标缓冲区。因此,如果源字符串长度大于或等于num,则不会复制终止NULL字节,并且结果将不会包含NULL终止字节,这很危险。

建议改用strlcpy。


strncpy()的语义,即使在上面的C ++参考文献中进行了精确解释时,也被普遍误解了。 此功能的行为是违反直觉的,并且容易出错。

为了避免在使用它或进一步开发过程中出现问题,当维护人员会误读代码并添加更多小错误时,有一个简单的解决方案:永远不要使用此功能。

您可以在Bruce Dawson的这篇文章中阅读有关此内容的更多详细信息。

要回答您的问题:如果源字符串的长度大于作为第三个参数传递的大小(通常对应于目标缓冲区的大小),则该函数会将大小字符复制到目标,并且这些字符中将不存在空字节。 然后,调用strlen(destination);将调用未定义的行为,因为它将尝试读取数组末尾之外的内容,直到找到空终止符为止。 这种特定的行为使strncpy易于出错。