问题介绍:
sprintf():格式化输出到指定的buffer中,当年刚刚知道这个函数的时候喜欢的不得了,不考虑执行效率的情况下,很多的字符串相关操作都可以用这个函数搞定。
后来知道了snprintf(),比前者多了一个参数,一直认为仅仅是前者的安全版本。
最近发现了一个两者不同的地方,直接看代码:
#include <stdio.h>
int main()
{
char cp1[255];
snprintf(cp1, 255, "%s", "hello");
printf("%s\n", cp1);
snprintf(cp1, 255, "%s_%s", cp1, "world");
printf("%s\n", cp1);
char cp2[255];
sprintf(cp2, "%s", "hello");
printf("%s\n", cp2);
sprintf(cp2, "%s_%s", cp2, "world");
printf("%s\n", cp2);
}
输出如下:
从输出的结果中看到在buffer即作为输入又作为输出的时候snprintf的结果与预期不同。
问题探究:
为了搞清楚原因去查阅一下C语言函数库的源码。
ftp://ftp.gnu.org/ > gnu > glibc > 下载 glibc-2.9.tar.gz
snpritnf.c 发现其封装了 __vsnprintf();
vsnprintf.c中找到后者实现如下:
int
_IO_vsnprintf (string, maxlen, format, args)
char *string;
_IO_size_t maxlen;
const char *format;
_IO_va_list args;
{
_IO_strnfile sf;
int ret;
#ifdef _IO_MTSAFE_IO
sf.f._sbf._f._lock = NULL;
#endif
if (maxlen == 0)
{
string = sf.overflow_buf;
maxlen = sizeof (sf.overflow_buf);
}
_IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
_IO_JUMPS ((struct _IO_FILE_plus *) &sf.f._sbf) = &_IO_strn_jumps;
string[0] = '\0';
_IO_str_init_static_internal (&sf.f, string, maxlen - 1, string);
ret = INTUSE(_IO_vfprintf) ((_IO_FILE *) &sf.f._sbf, format, args);
if (sf.f._sbf._f._IO_buf_base != sf.overflow_buf)
*sf.f._sbf._f._IO_write_ptr = '\0';
return ret;
}
第24行可以看出,snprintf()在处理时,首先将接收buffer的首位赋值成了 '\0' ,这就解释了为什么输入输出使用同一个buffer时,输入的字符串消失了的现象。
对比验证,再看一下sprintf()的源码,定位到 iovsprintf.c 的 __IO_vsprintf():
int
__IO_vsprintf (char *string, const char *format, _IO_va_list args)
{
_IO_strfile sf;
int ret;
#ifdef _IO_MTSAFE_IO
sf._sbf._f._lock = NULL;
#endif
_IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
_IO_JUMPS ((struct _IO_FILE_plus *) &sf._sbf) = &_IO_str_jumps;
_IO_str_init_static_internal (&sf, string, -1, string);
ret = INTUSE(_IO_vfprintf) ((_IO_FILE *) &sf._sbf, format, args);
_IO_putc_unlocked ('\0', (_IO_FILE *) &sf._sbf);
return ret;
}
从 snprintf() 的源码中可以看到,在调用 _IO_str_init_static_internal() 之前并没有将输出buffer的首位赋值 '\0' 。
总结:
通过对比 sprintf() 和 snprintf() 的源代码,解释了使用同一个buffer(buffer_a)作为这两个函数的输入和输出时,输出结果不同的原因。
因为 snprintf() 在执行时会先将作为输出的buffer_a首位赋值为 '\0' ,这使得作为输入的buffer_a在之后的赋值操作中成了空字符串,而 sprintf() 实现时没有这个操作。