printf,sprintf,vsprintf,vsnprintf的区别

最熟悉的不用说就是printf了,居然有了printf为什么还要有别的输出函数哪?当然是因为有不同的用途。

1.printf和sprintf的区别

先看看两个函数的原型

/* Write formatted output to stdout.  */
extern int printf (const char *__restrict __format, ...);
/* Write formatted output to S.  */
extern int sprintf (char *__restrict __s,
		    const char *__restrict __format, ...) __THROWNL;

sprintf比printf多了一个参数,也就是第一个参数char __restrict __s,其实就是个char类型的字符串。而且源码注释也解释的非常清楚——Write formatted output to S.也就是结果会保存到用户传入的第一个字符串参数里面。相对于是一个缓存。而printf源码注释也解释的非常清楚——Write formatted output to stdout.——是直接输出到stdout,也就是标准输出。

看下面的例子就能明白sprintf的用法:

int main(int argc, char *argv[]) {
    char buff[15];
    int a=10;
    sprintf(buff,"sprintf %d\n",a);
    return 0;
}

输出:

buff size 11

通过调用sprintf后,buff里面确实有值了,而且size是11。而且不会把buff的内容输出到屏幕(标准输出stdout)上。写到这sprintf的语法已经全部讲完了,sprintf唯一的作用就是向char *buff里面写内容。不输出我怎么知道里面有内容呢?你可以debug,也可以用printf输出,但是对于后者,可能是多此一举。看下面的例子

int main(int argc, char *argv[]) {
    char buff[15];
    int a=10;
    sprintf(buff,"sprintf %d\n",a);
    printf("buff %s\n",buff);
    return 0;
}

输出:

buff sprintf 10

为什么说是多此一举?因为sprintf就是不是用来打印输出到屏幕用的,他就是专门用来输出内容到char* buff里面的。printf才是专门用来输出到屏幕上的。我们可以看下printf的实现。

int
__printf (const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = __vfprintf_internal (stdout, format, arg, 0);//输入了stdout
  va_end (arg);

  return done;
}

在__vfprintf_internal这个函数调用的地方传入了stdout.也就是标准输出对象,所以printf默认就是用来向屏幕输出的。

小结:

printf是专门用来先屏幕输出的,也就是标准输出stdout。而sprintf就是专门用来输出内容到字符串的。

2.vsprintf和sprintf的区别

前面已经解释了sprintf和printf的区别,那么这个vsprintf又是做什么用的?先来看下两者的声明:

/* Write formatted output to S.  */
extern int sprintf (char *__restrict __s,
		    const char *__restrict __format, ...) __THROWNL;
/* Write formatted output to S from argument list ARG.  */
extern int vsprintf (char *__restrict __s, const char *__restrict __format,
		     __gnuc_va_list __arg) __THROWNL;

两者唯一的区别就是第三个参数,sprintf的第三个参数是可变列表,而vsprintf的第三个参数是__gnuc_va_list __arg,这是个什么东西?其实这也是可变列表,只是形式不一样而已。后者实际上是专门为C语言默认的可变列表做了封装,使之操作更灵活。相对于你可以给可变参数当成一个变量,这样就有更多的操作空间了。注释也就是的非常清楚——Write formatted output to S from argument list ARG.——格式化输出到字符串中,内容来源是参数列表,也就是__gnuc_va_list __arg。

要想再解释的更加清楚就不得不提stdarg.h这个头文件。这个头文件就是用来操作可变列表的。
在stdarg.h中__gnuc_va_list被typedef成va_list,比较原来的太长了不方便。

typedef __gnuc_va_list va_list;

到目前为止,我们只需要知道va_list约等于可变参数符号…(三个点)。来看下面的例子:

int main(void) {
    double s, t;
    s = sum(3, 1.1, 2.5, 13.3);
    t = sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1);
    printf("return value for "
           "sum(3, 1.1, 2.5, 13.3): %g\n", s);
    printf("return value for "
           "sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1): %g\n", t);
    return 0;
}

double sum(int lim, ...) {
    va_list ap; // 声明一个对象储存参数
    double tot = 0;
    int i;
    //va_start(va_list ap, last_arg),last_arg表示参数列表前一个参数,这里是lim
    va_start(ap, lim); // 把ap初始化为参数列表
    for (i = 0;i < lim;i++)
        tot += va_arg(ap, double); // 访问参数列表中的每一项
    va_end(ap); // 清理工作
    return tot;
}

输出:

return value for sum(3, 1.1, 2.5, 13.3): 16.9
return value for sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1): 31.6

double sum(int lim,…)第一个参数传入一个int类型,第二个参数传可变参数。va_list ap声明一个可变参数类型的变量,va_start(ap,lim)对va_list就行初始化,到这里va_list ap 已经完成了对可变参数…的读取。然后就可以操作可变参数的内容了。很明显转变为va_list的一个好处就是可以用for循环了,如果是传统的可变参数的话是不可以的。

上面的例子并没有用到vsprintf,只是为了让大家了解va_list是这么用的。vsprintf()里面的V就是指的va_list或者Variable arguments(stdarg.h开头注释写着)。

下面将上面的例子改动下来声明vsprintf的用法。

#include "../apue.h"
#include "stdarg.h"
double sum(int lim, ...);

int main(void) {
    double s, t;
    s = sum(3, 1.1, 2.5, 13.3);
    t = sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1);
    printf("return value for "
           "sum(3, 1.1, 2.5, 13.3): %g\n", s);
    printf("return value for "
           "sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1): %g\n", t);

    return 0;
}

double sum(int lim, ...) {
    va_list ap; // 声明一个对象储存参数
    double tot = 0;
    int i;
    va_start(ap, lim); // 把ap初始化为参数列表
    char buff[30];//如果数组的值太小会报*** stack smashing detected ***: terminated
    vsprintf(buff,"result:------%g %g %g %g %g\n",ap);
    printf("buff %s\n",buff);
    va_end(ap); // 清理工作
    return tot;
}

注意里面的vsprintf函数,有5个%占位符,只有一个输出变量ap。
小结:

vsprintf就是为了更方便的将可变参数输出到字符串缓存。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值