VA_LIST变参函数问题浅析

在之前Maxiee 的学习中,遇到声明函数的时候,都会在脑子里想一想,我们要设计的这个函数,都需要外界给它哪几个参数呢?有时候难以决定,总觉得自己设的参数有点罗嗦了,使算法也便罗嗦。但有一点是肯定的,就是函数的参数有几个、每个是干什么的,要在声明函数的时候确定死,总不能等到函数运行时再来确定吧!

不过今天Maxiee 在学习编程的时候,还真就看到一种函数,它的参数是可变的!他是这样声明的:

char *make_message(const char *fmt, ...);

还带有省略号!!Maxiee 抱着试一试的心态编译了一下,还真的通过了!

程序显示效果如下:

va_list

搜了一下,发现C语言库里专门有这样一个头文件,用来处理变参数问题:

#include <stdarg.h>

说是用来解决函数的重载问题,Maxiee 似懂非懂的,以前在学C++ 的时候,做过一些重载的题目,感觉都已经忘记了。

回到Maxiee 的程序里,Maixee发现,从功能上来理解,这个也不太难:

make_message这个函数,在程序里是这样调用的:

char* str = make_message("%d,%d,%d,%d",5,6,7,8);

显然,现在有了四个参数了。有没有发现,这个参数很类似传给printf的那种参数啊?

Maxiee 透露一下make_message 函数的功能,我们就恍然大悟了,它的返回值为“5,6,7,8”。

这不就是sprintf(str,"%d,%d,%d,%d",5,6,7,8)嘛!但是等一等,我们发现这个sprintf 的参数也是可变的,它的原型是:

int sprintf( char *buffer, const char *format [, argument] ... );

对吧?说白了,它也是通过stdarg.h 来处理的。我们的这个make_message ,相当用重复造了一下车轮。

Maxiee 打开make_message ,看看它是怎样定义的:

[c]

//这是stdarg.h 中的结构,用来处理变参的

va_list ap;

&nbsp;

//我们申请的这个内存,就是要返回的那个字符串

//它的最终值,就是“5,6,7,8”。

if ((p = (char *)malloc(size)) == NULL)

return NULL;

&nbsp;

while (1)

{

/* 尝试在申请的空间中进行打印操作 */

//获取可变参数列表的第一个参数的地址

va_start(ap, fmt);

//获得可变的参数,格式化输出到p,返回字符串的字节数,本例中就是7。

//<strong><span style="color: #ff0000;">说白了,我们就是在内部偷偷执行了一个sprintf</span></strong>

//<strong><span style="color: #ff0000;">根本不算重复造车轮= =||</span></strong>

n = vsnprintf (p, size, fmt, ap);

va_end(ap);

printf("%dn",n);

[/c]

 示例程序及注释如下:

[c]

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

char *make_message(const char *fmt, ...)
{

/* 初始时假设我们只需要不超过100字节大小的空间 */
int n, size = 100;
char *p;

va_list ap;

if ((p = (char *)malloc(size)) == NULL)
return NULL;

while (1)
{
/* 尝试在申请的空间中进行打印操作 */
va_start(ap, fmt);
n = vsnprintf (p, size, fmt, ap);
va_end(ap);
printf("%dn",n);

/* 如果vsnprintf调用成功,返回该字符串 */
if (n > -1 && n < size)
return p;

/* vsnprintf调用失败(n<0)或者p的空间不足够容纳size大小的字符串(n>=size),尝试申请更大的空间/
size *= 2; /
两倍原来大小的空间 */
if ((p = (char *)realloc(p, size)) == NULL)
return NULL;
return p;
}
}

int main()
{
/* 调用上面的函数 /
char
str = make_message("%d,%d,%d,%d",5,6,7,8);
printf("%sn",str);
// we allocate the memory in the make_message function, so we should release it by caller(main function).
free(str);
return 0;
}

[/c]