C语言:可变参数的函数(VA使用说明)

来源:互联网 发布:但丁数据 编辑:程序博客网 时间:2024/04/28 12:55
 

可变参数的函数(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 )
这个宏定义,目的只有一个,就是释放内存。