函数定义:
int snprintf(char*str, size_t size,constchar*format, ...);
函数说明:
最多从源串中拷贝size-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为size的话,将不会溢出。
函数返回值:
若成功则返回欲写入的字符串长度,若出错则返回负值。返回值并不是真正写入字符串的大小。
解释如下:
size是限定最终生成的dest的字符数,最多拷贝size-1个字符; 一般情况下size会取sizeof(dest),这是为了dst不溢出.
在snprintf(dest, size, "str: %s\n", src)中如果size-1大于等于"str: %s\n"的长度,则把"str: %s\n"都拷贝到dst; 如果size-1小于"str: %s\n"的长度,则从"str: %s\n"拷贝size-1长度的字符串到dst,并且末尾置\0.
就是说,拷贝的长度是size-1和源字符串长度的最小值;
错误使用snprintf函数返回值
代码示例
int main()
{
char buf[10] = "";
char src[30] = "hello world! hello world!";
int len = snprintf(buf, sizeof(buf), "%s", src);
printf("return len=%d\n", len);
buf[len] = '\0';
printf("buf=%s, bufLen=%d\n", buf, strlen(buf));
return 0;
}
现象&后果
上述代码运行时返回的len是25,写buf[len]='\0'时将出现错误。
Bug分析
snprintf函数返回的是预写入的字符串长度。以上述代码为例,如果源字符串src的长度小于等于sizeof(buf)-1,返回值则为实际写入目标字符串buf的字符数,也就是src的长度;如果src的长度大于sizeof(buf)-1,实际写入buf的字符数为sizeof(buf)-1,但返回值依然为src的长度。但snprintf返回值不一定就是src的长度,例如,当格式化字符串包含其他字符时,如上面的"%s"改为"ab=%s",返回值就比src的长度要大。实际上snprintf返回值可以理解为当buf大小没有限制时写到buf的字符个数。
在例子中len=25,buf[25]='\0'产生写越界,从而产生段错误,可能导致重大问题。上述代码实际上是想在snprintf复制完之后显式地在buf结尾处添加一个'\0'。但实际上,snprintf函数在复制结束时自动就会处理字符串结束标志'\0'的问题,不用额外处理。
snprintf函数的返回值常被用来和目标字符串buf的大小进行比较,以判断此次复制是否完全。
int main()
{
char buf[10] = "";
char src[30] = "hello world! hello world!";
int len = snprintf(buf, sizeof(buf), "%s", src);
printf("return len=%d\n", len);
if(len>sizeof(buf)-1)
{
printf("[Error] Source string length is %d. The buf size %d is not enough. Copy incomplete!\n", len, sizeof(buf));
}else{
printf("buf=%s, bufLen=%d\n", buf, strlen(buf));
}
return 0;
}
其实主要有两点:
1.函数返回值并不是实际写入字符串的大小,是欲要写入字符串的大小。
2.此函数会自动在字符串末尾补0,不需要再额外添加