首先,函数的参数以栈的形式存取,在存放时,从右到左开始入栈,所以在函数执行时,将函数的最左边第一个参数先入栈。因此可以以函数的参数列表的确定的最后一个参数的地址,通过指针移位和指定数据类型来获取函数的可变参数。
<stdarg.h>头文件中的相关宏定义
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
当前可变参数的地址加上下一可变参数对其的偏移量的就是下一可变参数的地址了(va_arg的实现)。这里提到的偏移量并不一定等于参数所占的字节数,而是为参数所占的字节数再扩展为机器字长(acpi_native_int)倍数后所占的字节数(因为入栈操作针对的是一个机器字),这也就是为什么_bnd那么定义的原因。
typedef char* va_list; //va_list表示可变参数列表类型
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) // va_start用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)
* 输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针(即,可变参数列表)
* 输入参数A: 为函数参数列表中最后一个固定参数
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) // va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数
* 输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
* 输入参数T: 正要处理的可变参数的类型
* 返回值: 当前可变参数的值
void va_end ( va_list ap ); //释放ap申请的空间
例:
#include 〈stdio.h〉
#include 〈string.h〉
#include 〈stdarg.h〉
/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
int demo( char, ... );
void main( void )
{
demo("DEMO", "This", "is", "a", "demo!", "");
}
/*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
int demo( char msg, ... )
{
/*定义保存函数参数的结构*/
va_list argp;
int argno = 0;
char para;
/*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/
va_start( argp, msg );
while (1)
{
para = va_arg( argp, char);
if ( strcmp( para, "") == 0 )
break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
va_end( argp );
/*将argp置为NULL*/
return 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); //vsprintf将aptr按照format规定的格式输入到buffer里面
va_end(aptr);
return(ret);
}
int main()
{ int i = 5;
float f = 27.0;
char str[50] = "runoob.com";
vspfunc("%d %f %s", i, f, str);
printf("%s\n", buffer);
return(0);
}
让我们编译并运行上面的程序,这将产生以下结果:
5 27.000000 runoob.com