我有以下示例代码,模仿应用程序中的代码。
#include <iostream>
#include <string.h>
#include <cstring>
#include <atlstr.h>
using namespace std;
void test(char *s, int size)
{
//s = "";
int lens = strlen(s);
char *str1 = "(( State:0.000000 Std30c5 = State:T ) OR (( State:0.000000 Std30c6 = State:T ) OR (( State:0.000000 Std30c7 = State:T ) OR (( State:0.000000 Std30c8 = State:T ) OR (( State:0.000000 Std30c9 = State:T ) OR (( State:0.000000 Std30ca = State:T ) OR (( State:0.000000 Std30cb = State:T ) OR (( State:0.000000 Std30cc = State:T ) OR (( State:0.000000 Std30cd = State:T ) OR (( State:0.000000 Std30ce = State:T ) OR (( State:0.000000 Std30cf = State:T ) OR ( ...0.000000 = State:T ))))))))))))";
int len1 = strlen(str1);
strncpy(s, str1, 512);
int len = strlen(s);
}
int main()
{
char strDisplay[512] = "";
test(strDisplay, 512);
cout << strDisplay << endl;
system("pause");
return 0;
}
结果是: lenofstrtest = 523; lenofstr1 = 512;
strtest =“((状态:0.000000 Std30c5 =州:T)或((状态:0.000000 Std30c6 =州:T)或((状态:0.000000 Std30c7 =州:T)或((状态:0.000000 Std30c8 =州:T)或((状态:0.000000 Std30c9 =州:T)或((状态:0.000000 Std30ca =州:T)或((状态:0.000000 Std30cb =州:T)或((状态:0.000000 Std30cc =州:T )OR((状态:0.000000 Std30cd =状态:T)或((状态:0.000000 Std30ce =状态:T)OR((状态:0.000000 Std30cf =状态:T)或(... 0.000000 =状态:T))) )))))))))的ÌÌÌÌJ¢Š£OO )“
为什么strncpy会复制其他字符?
(这导致了一个问题,因为错误的strnlen会导致解包逻辑变得混乱!)
我想这与“strncpy bug 512 bytes”有关...请帮我理解这个bug。
答案 0 :(得分:1)
strncpy
没有添加终止&#39; \ 0&#39;截断字符串的字符会导致您遇到的问题。当字符串未正确终止时,它看起来更长,但实际看到的是在内存中缓冲区之后放置的数据。它可能会导致严重的问题。
而不是strncpy
你应该使用strlcpy
来正确终止字符串并返回源字符串的长度,你可以将其与缓冲区的长度进行比较,以了解字符串是否被截断或不。 strncpy
返回指向缓冲区指针的指针(由于你已经知道它并不是很有用 - 你把它作为第一个参数传递)并且没有告诉你是否发生了截断。 / p>
见man strlcpy:
维基百科上的strlcpy()和strlcat()函数复制和连接字符串 使用与snprintf(3)相同的输入参数和输出结果。他们 旨在更安全,更一致,更不容易出错 替换容易被滥用的函数strncpy(3)和 strncat函数(3)。 strlcpy()和strlcat()取全部大小 目标缓冲区,如果有空间,保证NUL终止。 请注意,NUL的空间应包含在dstsize中。
和C string handling - Replacements:
最受欢迎的[a]替代品是strlcat和strlcpy函数, 它于1998年12月出现在OpenBSD 2.4中。[84]这些功能 总是将一个NUL写入目标缓冲区,截断结果 如有必要,返回所需的缓冲区大小, 它允许检测截断并提供大小 创建一个不会截断的新缓冲区。
不幸的是,它不包含在glibc中 - 请参阅Secure Portability论文 达米恩米勒(PDF):
strlcpy和strlcat API正确检查目标缓冲区的边界, nul-terminate在所有情况下都返回源字符串的长度, 允许检测截断。此API已被大多数人采用 现代操作系统和许多独立软件包, 包括OpenBSD(起源于哪里),Sun Solaris,FreeBSD,NetBSD, Linux内核,rsync和GNOME项目。值得注意的例外 是GNU标准C库,glibc [12],其维护者 坚决拒绝包含这些改进的API,标记它们 尽管事先有证据证明他们是“可怜的低效BSD废话”[4] 大多数情况比它们取代的API更快[13]。结果是, 超过100个OpenBSD端口树中的软件包 保持自己的strlcpy和/或strlcat替换或等效 API - 不是理想的事态。
它可以在libbsd库中用于Linux:
Debian和Ubuntu以及其他发行版中都有软件包:
即使您不想依赖glibc以外的任何其他内容,也很容易添加到您的项目中,因为整个来源很短并且在许可许可下可用:
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
来源:http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11
答案 1 :(得分:0)
当您使用函数strncpy
时,您始终必须使用终止零附加它。例如
strncpy(s, str1, n );
s[n-1] = '\0';
否则代码将在您的情况下不安全。
考虑到将这两个标题放在一起没有任何意义
#include <string.h>
#include <cstring>
删除第一个标题,并仅使用C ++中的第二个标题。
#include <cstring>
答案 2 :(得分:0)
strDisplay应该至少分配513个单位,因为在strncpy中没有隐式添加空终止字符。
char strDisplay[513] = "";
strDisplay[512] = '\0'; //recommended
答案 3 :(得分:-1)
strncpy
是一个糟糕的函数,因为它不会生成字符串。如果您想使用C风格的字符串处理,那么snprintf
更容易安全使用:
snprintf(s, size, "%s", str1);
请注意,{+ 1}}在C ++中已弃用;您可以改用char *str1 = "...
。
答案 4 :(得分:-1)
在C ++中使用std::string
。它会自动为您解决所有这些问题。
#include <iostream>
#include <string>
std::string test()
{
return "(( State:0.000000 Std30c5 = State:T ) OR (( State:0.000000 Std30c6 = State:T ) OR (( State:0.000000 Std30c7 = State:T ) OR (( State:0.000000 Std30c8 = State:T ) OR (( State:0.000000 Std30c9 = State:T ) OR (( State:0.000000 Std30ca = State:T ) OR (( State:0.000000 Std30cb = State:T ) OR (( State:0.000000 Std30cc = State:T ) OR (( State:0.000000 Std30cd = State:T ) OR (( State:0.000000 Std30ce = State:T ) OR (( State:0.000000 Std30cf = State:T ) OR ( ...0.000000 = State:T ))))))))))))";
}
int main()
{
std::cout << test() << std::endl;
return 0;
}
注意不需要内存管理,临时魔术大小缓冲区或空终止符。