关于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.
这句话是什么意思?
-
长话短说:避免将strncpy用作安全的strcpy,这是出于不同目的的不同功能,这会带来其他安全风险。
-
这是故意标记为C ++吗?如果您使用的是C ++,我会完全避免使用C字符串函数,而应使用C ++ Std :: String函数。否则,我将删除C ++标记...
-
我不同意删除标签@James,尽管您不建议使用"适当的"字符串。但是,尽管绝大多数情况下使用C ++字符串可能会更好,但是在某些情况下,旧的C字符串可能有用,毕竟它们是C ++标准的一部分。
-
cplusplus.com是一个站点,其中包含措辞不佳,误导性的信息,而且通常是纯错误的信息。我会避免使用它,而是阅读相关的标准文档。
-
我反对cplusplus.com的建议。对于用C表示的任何内容,更喜欢来自opengroup.org的搜索结果,而不是cplusplus.com。与本例进行比较时,cplusplus.com忽略了针对重叠的源/目标未定义行为的情况。 Opengroup文本来自Posix,或多或少地复制了C标准。对于C ++,很难以网页形式找到良好的信息,因此,在网络上搜索所需的类/函数的名称,然后在书中查找它们。或标准的PDF。
-
@詹姆士。是的,实际上是故意使用了C ++标签。
这意味着,例如,如果您的源字符串为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视为字符串,则结果可能会令人失望。
-
"您可能会对结果感到失望"-是的,UB为何总是愚蠢的鼻恶魔,而不是蛋糕?非鼻蛋糕,即。
-
@Steve:哈哈,好人。实际上,这可能是整个问题的基础(尽管即使是CW,它也可能会被关闭):"有人经历过未定义好的行为,这是件好事吗?不,不仅是它满足了您的期望,而且还超越了职责范围。"
-
@Steve,即使UB生产蛋糕,它也会变得恶魔味,我很害怕
-
@bdonlan:或鼻味。
-
@paxdiablo。感谢您的回复。您能解释一下" d [10] = 0;"这一行吗?特别是当" s"太长时,位置" 10"是否已经被填充?那我怎么追加一个"空"呢?谢谢。
-
@SWEngineer:在这种情况下实际上不会填写d[10]。复制10个字节将填充d[0]至d[9](包括首尾),而将d[10]保留为空。这就是为什么您将其声明为int d[11](0至10)。更新了答案以澄清。
字符串"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结尾的字符串,则不可能知道其长度,并且大多数字符串操作函数都会失败或发生意外情况。
-
请不要* sizeof(char),它会增加击键次数,从而缩短键盘的寿命,而给显示器增加多余的电子量,这会降低寿命:-)
-
好的,也许应该是sizeof(hello[0])。
这意味着它将复制源字符串的终止null,但如果源字符串不适合目标,则不添加终止null。
这意味着只有num个字节将从源缓冲区复制到目标缓冲区。因此,如果源字符串长度大于或等于num,则不会复制终止NULL字节,并且结果将不会包含NULL终止字节,这很危险。
建议改用strlcpy。
-
由谁推荐?当然不是glibc bods :-)我的建议是学习您使用的工具的所有缺点,而不要依赖拐杖。希望达到那种安全水平的人们应该离开C的奇妙世界,然后回到Visual Basic :-)并且,请注意笑脸!我不能告诉你我干涩的幽默感从这里开始有多少论点:-)
strncpy()的语义,即使在上面的C ++参考文献中进行了精确解释时,也被普遍误解了。 此功能的行为是违反直觉的,并且容易出错。
为了避免在使用它或进一步开发过程中出现问题,当维护人员会误读代码并添加更多小错误时,有一个简单的解决方案:永远不要使用此功能。
您可以在Bruce Dawson的这篇文章中阅读有关此内容的更多详细信息。
要回答您的问题:如果源字符串的长度大于作为第三个参数传递的大小(通常对应于目标缓冲区的大小),则该函数会将大小字符复制到目标,并且这些字符中将不存在空字节。 然后,调用strlen(destination);将调用未定义的行为,因为它将尝试读取数组末尾之外的内容,直到找到空终止符为止。 这种特定的行为使strncpy易于出错。