snprintf的使用

函数定义:

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,不需要再额外添加

你可能感兴趣的:(C语言)