当前位置: 代码迷 >> 综合 >> printf,sprintf,vsprintf,vsnprintf的区别
  详细解决方案

printf,sprintf,vsprintf,vsnprintf的区别

热度:87   发布时间:2024-01-10 20:50:30.0

最熟悉的不用说就是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);//输入了stdoutva_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表示参数列表前一个参数,这里是limva_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 ***: terminatedvsprintf(buff,"result:------%g %g %g %g %g\n",ap);printf("buff %s\n",buff);va_end(ap); // 清理工作return tot;
}

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

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

  相关解决方案