我正在使用以下代码:
char filename[ 255 ];
strncpy( filename, getenv( "HOME" ), 235 );
strncat( filename, "/.config/stationlist.xml", 255 );
收到此消息:
(warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.
(error) Dangerous usage of 'filename' (strncpy doesn't always null-terminate it).
答案 0 :(得分:6)
我通常会避免使用str*cpy()
和str*cat()
。您必须应对边界条件,神秘的API定义以及意外的性能后果。
您可以改用snprintf()
。您只需要与目标缓冲区的大小竞争。并且,它更安全,因为它不会溢出,并且将永远为你终止NUL。
char filename[255];
const char *home = getenv("HOME");
if (home == 0) home = ".";
int r = snprintf(filename, sizeof(filename), "%s%s", home, "/.config/stationlist.xml");
if (r >= sizeof(filename)) {
/* need a bigger filename buffer... */
} else if (r < 0) {
/* handle error... */
}
答案 1 :(得分:4)
您的filename
电话可能会导致strncat
超时。
使用:
strncat(filename, "/.config/stationlist.xml",
sizeof filename - strlen(filename) - 1);
还要确保在strncpy
调用后终止缓冲区:
strncpy( filename, getenv( "HOME" ), 235 );
filename[235] = '\0';
如果源的长度大于或等于要复制的最大字符数,则strncpy
不会终止其目标缓冲区。
答案 2 :(得分:3)
man strncpy
有这样的说法:
Warning: If there is no null byte among the first n bytes
of src, the string placed in dest will not be null terminated.
如果它在耗尽最大长度之前遇到源中的0字节,它将被复制。但是,如果在源中的第一个0之前达到最大长度,则不会终止目标。最好在strncpy()
返回后确保自己是...
答案 3 :(得分:2)
strncpy()
和(更多)strncat()
都有非显而易见的行为,你最好不使用它们。
如果您的目标字符串为了参数,长度为255个字节,strncpy()
将始终写入所有255个字节。如果源字符串短于255个字节,则将填充余数。如果源字符串长度超过255个字节,它将在255个字节后停止复制,使目标没有空终止符。
大多数'大小'函数的大小参数(strncpy()
,memcpy()
,memmove()
等)是目标字符串(内存)中的字节数。对于strncat()
,大小是已经在目标中的字符串结尾之后剩余的空间量。因此,当您知道目标缓冲区的大小(strncat()
)和目标字符串当前的长度(S
)时,您只能安全地使用L
。 strncat()
的安全参数是S-L
(我们会担心其他时间是否有一个异地)。但是,如果您知道L
,则strncat()
跳过L
字符是没有意义的;您可以通过target+L
作为开始的地方,并简单地复制数据。您可以使用memmove()
或memcpy()
,也可以使用strcpy()
,甚至strncpy()
。如果您不知道源字符串的长度,您必须确信截断它是有意义的。
char filename[255];
strncpy(filename, getenv("HOME"), 235);
strncat(filename, "/.config/stationlist.xml", 255);
除非大小太小(或者你在未设置$HOME
的情况下运行程序),否则第一行是无法使用的,但这超出了这个问题的范围。对strncpy()
的调用不会使用sizeof(filename)
作为大小,而是使用任意小的数字。它不是世界末日,但通常不能保证变量的最后20个字节是零字节(或者甚至是任何一个都是零字节)。在某些情况下(filename
是一个全局变量,以前未使用过),可能会保证零。
strncat()
调用尝试在filename
中的字符串末尾添加24个字符,这些字符可能已经是232-234字节长,或者可能任意长于235字节。无论哪种方式,这都是有保证的缓冲区溢出。 strncat()
的使用也直接落入关于其大小的陷阱。您已经说过,在filename
已经存在的内容之后最多可以添加255个字符,这显然是错误的(除非来自getenv("HOME")
的字符串恰好为空)。
更安全的代码:
char filename[255];
static const char config_file[] = "/.config/stationlist.xml";
const char *home = getenv("HOME");
size_t len = strlen(home);
if (len > sizeof(filename) - sizeof(config_file))
...error file name will be too long...
else
{
memmove(filename, home, len);
memmove(filename+len, config_file, sizeof(config_file));
}
会有人坚持认为'memcpy()
是安全的,因为字符串不能重叠',并且在一个级别上他们是正确的,但重叠应该是非问题的,并且memmove()
,这不是问题。所以,我一直使用memmove()
......但是我还没有完成时序测量,看它有多大问题,如果它根本就是问题。也许其他人已经完成了测量。
strncat()
。strncpy()
(注意它在非常大的缓冲区上的行为!)。memmove()
或memcpy()
代替;如果你能安全地复制,你就知道了使这个变得合理的大小。答案 4 :(得分:1)
1)你的strncpy不一定是null-terminate filename。实际上,如果getenv(“HOME”)长于235个字符且getenv(“HOME”)[234]不是0,则不会。 2)你的strncat()可能会尝试将文件扩展名超过255个字符,因为正如它所说的那样,
3rd parameter is the maximum number of characters to append.
(不是dst允许的总长度)
答案 5 :(得分:0)
strncpy(Copied_to,Copied_from,sizeof_input)
在字符数组之后输出垃圾值(不用于字符串类型)。要使用遍历字符数组的 for 循环而不是简单地使用 cout<<var;
for(i=0;i<size;i++){cout<<var[i]}
我找不到使用 minGW 编译器在 Windows 系统上进行遍历的解决方法。 空终止并不能解决问题。在线编译器工作正常。