关于sprintf,printf,这些函数,参数都是不固定的。而这样编程方式,也可以自己来搞定。c语言提供了几个宏就是做这个的。
va_list //一个很特殊的类型
type va_arg(va_list ap, type);
void va_start(va_list ap, last_arg);
void va_end(va_list ap);
void va_copy(va_list dest,va_list src);
这个va_list是一个完整的对象类型,适用于保存宏va_start,va_copy,va_arg和va_end所需的信息。
如果创建了一个va_list实例,传递给另一个函数,并通过该函数中的va_arg使用,则在调用函数中的任何后续使用都应该在调用va_end之前进行。
先看一个最简单的例子
#include <stdarg.h>
int Sum(int n, ...) {
int i;
int sum = 0;
va_list mark;
//va_list sub;
va_start(mark, n);
//va_copy(sub, mark);
printf("n is %d\n", n);
for (i = 0; i < n; ++i) {
int tmp = va_arg(mark, int);
//int tmp = va_arg(sub, int);
sum += tmp;
printf("val is %d\n", tmp);
}
va_end(mark);
//va_end(sub);
return sum;
}
int main() {
int ret = Sum(5, 1, 2, 3, 4, 5);
printf("\nsum is %d", ret);
return 0;
}
可以简单理解va_start就是初始化,其第二个参数 last_arg在本例中可发现就是“n”,这个就是最后接收到的固定参数。为什么是最后接收到,是因为参数的压栈顺序,这个可自行google。
另外,我把va_copy的注释起来了,这个就是简单的拷贝。然后va_end看头文件就是定义为((void)(ap = (va_list)0))。也没什么好说的。
但是这样简单应用看起来也没啥用。下面的写法便会让人一目了然。
#include <stdio.h>
#include <stdarg.h>
char buffer[80];
int vspfunc(char* format, ...)
{
va_list aptr;
int ret;
va_start(aptr, format);
ret = vsprintf(buffer, format, aptr);
printf("test1 %d\n", va_arg(aptr, int));
printf("test2 %d\n", va_arg(aptr, int));
printf("test3 %s\n", va_arg(aptr, char*));
va_end(aptr);
return(ret);
}
int main()
{
int i = 5;
int f = 27;
char str[50] = "tutoriasyiibai.com";
vspfunc("%d %d %s", i, f, str);
printf("\n\n%s", buffer);
return(0);
}
这就好像sprintf一样,使用vsprintf,我把这3部分黏贴到了buffer中。这对于像做日志系统等的还是有些用处的。
需要注意的是,我在vspfunc里三个printf,我本来看va_arg菜鸟驿站的解释(这个宏检索函数参数列表中类型为 type 的下一个参数),还以为顺序不重要,对这个例子,因为只有一个char*,所以怎么都可以打印出来,而int有两个,所以那俩int才有顺序。
试了下发现,先打印char*还是会报错的。还是要按照顺序来的。看了下头文件的定义,发现原来并没有搞什么缓存之类的,所以我这个例子,char*和int所占空间的不同,由于读取顺序的错误必然出错。还是不能想当然啊。