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

时间:2022-10-25 12:43:04

转自:http://blog.csdn.net/raito__/article/details/48860119

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

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

例如:

[cpp] view plain copy
  1. int a=1,b=2;  
  2. printf("a=%d,b=%d",a,b);  
 
输出: 
 

[cpp] view plain copy
  1. a=1,b=2  


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

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

例如:

[cpp] view plain copy
  1. int a=1,b=2;  
  2. char s[10];  
  3. sprintf(s,"a=%d,b=%d",1,2);  
  4. puts(s);  


输出:

[cpp] view plain copy
  1. a=1,b=2  

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

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

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

例如:

[cpp] view plain copy
  1. void Myprintf(const char* fmt,...);  
  2.   
  3. int a=1,b=2;  
  4. char s[10];  
  5. Myprintf("a=%d,b=%d",a,b);  
  6.   
  7. void Myprintf(const char* fmt,...)  
  8. {  
  9.   char s[10];  
  10.   va_start(ap, fmt);      
  11.   vsprintf(s,fmt,ap);  
  12.   va_end(ap);     
  13.   puts(s);  
  14. }  


输出:

[cpp] view plain copy
  1. a=1,b=2  

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

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

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

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


(错误的)代码:

[cpp] view plain copy
  1. void Myprintf(const char* fmt,...)  
  2. {  
  3.   char s[10];  
  4.   sprintf(s,fmt);  
  5.   puts(s);  
  6.   puts(s);  
  7. }  

当你尝试这么调用该函数时:

[cpp] view plain copy
  1. int a=1,b=2;  
  2. Myprintf("a=%d,b=%d",a,b);  

输出:

[cpp] view plain copy
  1. a=?,b=?  
  2. 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()那么这个问题就可以迎刃而解。

函数如下:

[cpp] view plain copy
  1. void Myprintf(const char* fmt,...)  
  2. {  
  3.   char s[10];  
  4.   va_list ap;  
  5.   va_start(ap,fmt);  
  6.   vsprintf(s,fmt,ap);  
  7.   va_end(ap);  
  8.   puts(s);  
  9.   puts(s);  
  10. }  


当你这么调用时:

[cpp] view plain copy
  1. Myprintf("a=%d,b=%d",a,b);  

输出:

[cpp] view plain copy
  1. a=1,b=2  
  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 希望大家发现错误帮我指正,不幸感激!最后给懒得敲代码的童鞋附上这段演示代码:

[cpp] view plain copy
  1. #include <stdarg.h>  
  2. void Myprint(const char* fmt,...);  
  3.   
  4. int main()  
  5. {  
  6.     int a=1,b=2;  
  7.     Myprintf("a=%d,b=%d",a,b);  
  8.     return 0;  
  9. }  
  10.   
  11. void Myprintf(const char* fmt,...)  
  12. {  
  13.   char s[10];  
  14.   va_list ap;  
  15.   va_start(ap,fmt);  
  16.   vsprintf(s,fmt,ap);  
  17.   va_end(ap);  
  18.   puts(s);  
  19.   puts(s);  
  20. }