函数原型:printf(const char *format,[argument]);
例如:
int a=1,b=2; printf("a=%d,b=%d",a,b);输出:
a=1,b=2
函数原型: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
需要引入相关头文件 #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 是什么东东一会儿再解释,先往下看。
假设你想封装一个子函数 Myprintf() ,其功能是将格式化字符串输出两遍,若用 sprintf() 实现:
(错误的)代码:
void Myprintf(const char* fmt,...) { char s[10]; sprintf(s,fmt); puts(s); puts(s); }
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()那么这个问题就可以迎刃而解。
函数如下:
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() 为了能够解析你传给它的多个参数,你必须告诉它参数从哪里开始。
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); }