可变参数的函数(VA使用说明)
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
void va_start( va_list arg_ptr, prev_param ); (ANSI version)
例子:
//include header file
#include <stdio.h>
#include <stdarg.h>
//函数声明
int average( int first, ... );
void main( void )
{
/* Call with 3 integers (-1 is used as terminator). */
printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );
/* Call with 4 integers. */
printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );
/* Call with just -1 terminator. */
printf( "Average is: %d\n", average( -1 ) );
}
int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
{
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
//结果
Average is: 3
Average is: 8
Average is: 0
再看一个例子:
//include header file
#include <stdio.h>
#include <stdarg.h>
#define bufsize 80
#define buffer[bufsize];
/*
*格式化带参数的字符串
*/
int vspf(char *fmt, ... )
{
va_list argptr;
int cnt;
va_start(argptr,fmt);
cnt = vsnprintf(buffer,bufsize,fmt,argptr);//将带参数的字符串按照参数列表格式化到buffer中
va_end(argptr);//结束变量列表,和va_start成对使用
return cnt;
}
int main(int argc,char *argv[])
{
int inumber = 30;
float fnumber = 90.0;
char string[4] = "abc";
vspf("%d %f %s",inumber,fnumber,string);
printf("%s\n",buffer);
return 0;
}
原理分析:
代码:
void simple_va_fun(int i, ... )
{
va_list argptr;
int j=0;
va_start(argptr,i);
j=va_arg(argptr,int);
va_end(argptr);
printf("%d %d",i,j);
return;
}
simple_va_fun(100)
结果:100 -123456789(不确定值)
simple_va_fun(100,200)
结果:100 200
simple_va_fun(100,200,300)
结果:100 200
分析:(stdarg.h)
原始定义如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
宏定义说明:
_INTSIZEOF(n)
这个宏定义主要是取得不变参数的长度(sizeof(n)).
(sizeof(n) + sizeof(int) - 1) &~(sizeof(int) - 1) 这种写法,主要目的是为了内存的对齐
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
这个宏定义,执行后,便可得到第一个可变参数的地址。说明如下:
C语言参数的压栈顺序是,从右向左,因此有如下的内存结构。
|高地址---------------------—|
|返回地址--------------------|
|。。。。。。----------------|
|可变参数前面的固定参数地址--|----->(va_list)&v
|第一个可变参数--------------|----->(va_list)&v + _INTSIZEOF(v)
|。。。。。。----------------|
|最后一个可变参数------------|
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
这个宏定义执行后,可以顺序得到可变参数的值。
说明:(ap += _INTSIZEOF(t)),这句话有两个地方需要注意:
注意一:ap+= ,实际上执行后 ap(即:可变参数的指针已经改变为了下一个可变参数的地址)
注意二:- _INTSIZEOF(t),执行减动作后,才得到你想要得到的地址。
这样做,有些复杂了。但是为了地址自加,且返回对应的值,这样做是合理的。
可参考keil中的这个宏定义实现方法:#define va_arg(ap,t) (((t *)ap)++[0])
#define va_end(ap) ( ap = (va_list)0 )
这个宏定义,目的只有一个,就是释放内存。