sprintf和snprintf 在同一buffer既是输入又是输出时的不同表现

问题介绍:

        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() 实现时没有这个操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值