strncpy不';不总是空终止
我使用的代码如下:strncpy不';不总是空终止,c,strncpy,C,Strncpy,我使用的代码如下: 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) Dang
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).
您的
strncat
调用可能会溢出filename
使用:
另外,请确保在调用strncpy
后将缓冲区终止为空:
strncpy( filename, getenv( "HOME" ), 235 );
filename[235] = '\0';
由于
strncpy
不为空,因此,如果源的长度大于或等于要复制的最大字符数,则终止其目标缓冲区。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()返回…1)您的strncpy不一定为空终止文件名之后是您自己。事实上,如果getenv(“HOME”)长度超过235个字符,并且getenv(“HOME”)[234]不是0,那么它就不会是0。
2) 您的strncat()可能会尝试将文件名扩展到255个字符以外,因为正如它所说
3rd parameter is the maximum number of characters to append.
(不是dst的总允许长度)我通常避免使用
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... */
}
char文件名[255];
const char*home=getenv(“home”);
如果(home==0)home=“.”;
int r=snprintf(文件名,sizeof(文件名),“%s%s”,home,“/.config/stationlist.xml”);
如果(r>=sizeof(文件名)){
/*需要更大的文件名缓冲区*/
}else如果(r<0){
/*处理错误*/
}
两个strncpy()
和(更重要的是)strncat()
都有不明显的行为,最好不要使用它们
strncpy()
如果您的目标字符串长度为255字节(为了参数),则strncpy()
将始终写入所有255字节。如果源字符串短于255个字节,它将对剩余的字符串进行零填充。如果源字符串长度超过255字节,则在255字节后将停止复制,使目标字符串没有空终止符
strncat()
大多数“size”函数(strncpy()
、memcpy()
、memmove()
)等)的大小参数是目标字符串(内存)中的字节数。使用strncat()
,大小是目标中已经存在的字符串结束后剩余的空间量。因此,只有当您知道目标缓冲区有多大(S
)以及目标字符串当前有多长(L
)时,才能安全地使用strncat()。然后,strncat()
的安全参数是S-L
(我们将担心在其他时间是否有一个off)。但是如果您知道L
,那么让strncat()
跳过L
字符是没有意义的;您可以将target+L
作为起点,然后简单地复制数据。您可以使用memmove()
或memcpy()
,也可以使用strcpy()
,甚至strncpy()
。如果您不知道源字符串的长度,您必须确信截断它是有意义的
问题代码的分析
第一行是无懈可击的,除非大小被认为太小(或者您在未设置$HOME
的上下文中运行程序),但这超出了这个问题的范围。调用strncpy()
不会使用sizeof(filename)
作为大小,而是使用任意小的数字。这并不是世界末日,但不能保证变量的最后20个字节通常是零字节(甚至其中任何一个都是零字节)。在某些情况下(filename
是一个全局变量,以前未使用过),可以保证零
strncat()
调用尝试在filename
中的字符串末尾追加24个字符,这些字符可能已经是232-234字节长,或者可能任意超过235字节。无论哪种方式,这都是有保证的缓冲区溢出。strncat()
的使用也直接陷入了关于其大小的陷阱。您已经说过,在文件名
中已有字符的末尾之外再加上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()
;如果你能安全地复制,你就知道使这变得合理所需的尺寸strncpy(复制到、复制自、输入大小)
在字符数组后输出垃圾值(不用于字符串类型)。要解决它的输出,请使用遍历字符数组的for循环,而不是简单地使用coutwhat是您的实际问题?很好奇,但是哪个编译器给出了这些消息?@andrewcooke谷歌说它是cppcheck
静态分析器谢谢。看起来很有用。getenv()的危险用法:它可能返回NULL,并导致str[n]cpy()的危险用法;如果filename是一个自动变量,则不会对其进行初始化(或者您称之为零)
char filename[255];
strncpy(filename, getenv("HOME"), 235);
strncat(filename, "/.config/stationlist.xml", 255);
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));
}
for(i=0;i<size;i++){cout<<var[i]}