一般的函数在传参时只能传一个或两个甚至多个参数,但其前提是参数的个数必须是固定不变的。printf()函数和scanf()函数的参数却是可变的。在printf()函数中,它所传的参数是由标准化格式输出的类型决定。
例:
int main()
{
printf("%s\n", "hello world");
printf("%s%s%c", "hello ", "worl", 'd');
system("pause");
return 0;
}
这段代码中,第一个printf有一个%s代表其参数只有一个,而第二个有三个分别是%s%s%c则代表有三个参数。两者结果相同。
对于scanf与printf相类似。
而对一般的函数要如何来做到这一点呢?下面我就来简单介绍一下可变参数列表来实现这一功能。
int average(int n, ...)
{
va_list arg;
//typedef char * va_list;
int i = 0;
int sum = 0;
va_start(arg, n);
//#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
for (i = 0; i < n; i++)
{
sum += va_arg(arg, int);
//#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
}
return sum / n;
va_end(arg);
//#define _crt_va_end(ap) ( ap = (va_list)0 )
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int avg1 = average(2, a, c);
int avg2 = average(3, a, b, c);
printf("avg1 = %d\n", avg1);
printf("avg2 = %d\n", avg2);
system("pause");
return 0;
}
这段代码是用来求几个数的平均值。main函数中的这两句
int avg1 = average(2, a, c);
int avg2 = average(3, a, b, c);
中的2,3都是为后面函数在栈帧中寻找提供起始地址和控制循环次数。
而average函数中:
va_list arg;
//typedef char * va_list;表示arg是一个char型的指针,arg里面存的是一个地址。
va_start(arg, n);
//#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) );这段则表示将arg的地址赋值为位置参数列表中第一个未知参数。在函数栈帧中如图
其中intsizeof()函数的意思是向上取整。
va_arg(arg, int);
//#define _crt_va_arg(ap,t) ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) );即((int )((arg+=_INTSIZEOF(int))-_INTSIZEOF(int)))此时arg是一个int型指针,指向第二个未知参数,而表达式取的值是第一个未知参数的值。在函数栈帧中这样表示:
va_end(arg);
//#define _crt_va_end(ap) ( ap = (va_list)0 )
循环结束后给arg赋空指针。
在运用可变参数列表时,需要注意传n和传类型!va_arg这个表达式执行一次后就会指向下一个变量,不再是当前变量,在使用时需谨慎!可变参数列表在使用是只能从第一个挨个取直到最后一个,中间不能跳过几个变量!