C语言printf()、sprintf()、vsprintf()的区别与联系

from : https://blog.csdn.net/raito__/article/details/48860119

printf() 在控制台应用程序中最为常用,使用也很简单。其参数为格式化字符串。

函数原型:printf(const char *format,[argument]);

例如:

int a=1,b=2;  
printf("a=%d,b=%d",a,b);  

输出:a=1,b=2  

sprintf() 用于将输出存到字符缓冲中。

函数原型:sprintf(char *buffer, const char *format, [argument]);

例如:

int a=1,b=2;  
char s[10];  
sprintf(s,"a=%d,b=%d",1,2);  
puts(s);  

输出:a=1,b=2 

vsprintf() 与sprintf() 功能类似。

 

需要引入相关头文件 #include <stdarg.h>

函数原型: vsprintf(char *buffer, char *format, va_list param);

例如:

void Myprintf(const char* fmt,...);  
  
int a=1,b=2;  
char s[10];  
Myprintf("a=%d,b=%d",a,b);  
  
void Myprintf(const char* fmt,...)  
{  
  char s[10];  
  va_start(ap, fmt);      
  vsprintf(s,fmt,ap);  
  va_end(ap);     
  puts(s);  
}  

输出:a=1,b=2  

因此可以用 vsprintf() 来实现 sprintf()。

其中 va_start、va_end、va_list 是什么东东一会儿再解释,先往下看。

既然sprintf() 与 vsprintf() 功能类似,为什么不统一下,而且 vsprintf() 用法如此麻烦呢?

假设你想封装一个子函数 Myprintf() ,其功能是将格式化字符串输出两遍,若用 sprintf() 实现:

(错误的)代码:

void Myprintf(const char* fmt,...)  
{  
  char s[10];  
  sprintf(s,fmt);  
  puts(s);  
  puts(s);  
}  
  1. 当你尝试这么调用该函数时:
    int a=1,b=2;  
    Myprintf("a=%d,b=%d",a,b);  

输出:

a=?,b=?  

a=?,b=?  
我也不知道它会输出几,但一定不会是 a=1,b=2 。

这显然是错误的!

为什么呢?

当你这么调用 Myprintf() 时,其中的sprintf() 实际上是这样的:

sprintf(s,"a=%d,b=%d") 

而不是:

sprintf(s,"a=%d,b=%d",a,b) !!

因为这么调用的话只能解析到第一个字符串而无法解析到后面的参数。

所以类似这种封装用 sprintf() 是无法实现的,使用 sprintf() 只能原始的为它输入所有的参数而不能以传参的方式给它。

若用 vsprintf()那么这个问题就可以迎刃而解。

我们可以参考

#include <stdio.h>

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

#include <stdarg.h>

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vdprintf(int fd, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

函数如下:

void Myprintf(const char* fmt,...)  
{  
  char s[10];  
  va_list ap;  
  va_start(ap,fmt);  
  vsprintf(s,fmt,ap);  
  va_end(ap);  
  puts(s);  
  puts(s);  
}  

当你这么调用时:Myprintf("a=%d,b=%d",a,b); 
输出:

a=1,b=2

a=1,b=2  

成功了!哇哈哈哈哈!!想想也是有点小激动!!!

 

现在来解释一下这个 vsprintf():

首先你要知道函数执行时,函数的参数是倒序压入栈中的,vsprintf() 为了能够解析你传给它的多个参数,你必须告诉它参数从哪里开始。

vadefs.h 头文件中这么定义 :typedef char * va_list,于是我们定义了一个 va_list ap 来保存参数起始地址。

va_start(ap,fmt) 就找出这个函数在栈中排列的一堆参数的起始地址,然后直接浏览栈中参数,并用 vsprintf() 实现格式化字符串的读取,最后 vs_end(ap) 释放ap,就像释放指针一样。通俗地说就是因为 vsprintf() 比 sprintf() 更加接近底层(栈),因此能实现这个目的,也是因此能用 vsprintf() 来实现 sprintf()。

打的手好酸~0.0 希望大家发现错误帮我指正,不幸感激!最后给懒得敲代码的童鞋附上这段演示代码:

#include <stdarg.h>  
void Myprint(const char* fmt,...);  
  
int main()  
{  
    int a=1,b=2;  
    Myprintf("a=%d,b=%d",a,b);  
    return 0;  
}  
  
void Myprintf(const char* fmt,...)  
{  
  char s[10];  
  va_list ap;  
  va_start(ap,fmt);  
  vsprintf(s,fmt,ap);  
  va_end(ap);  
  puts(s);  
  puts(s);  
}  

 

  • 65
    点赞
  • 195
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值