1.缺省参数实现参数列表可变(差的方案)
当我们在写一个参数个数未知而类型确定的函数(如计算多个整型数字的平均数)时,可以用函数的第一个参数表示后面的参数个数,并定义多个缺省参数,在函数体内读取相应个数的参数。
#include <iostream>
using namespace std;
float average(int number, int a, int b = 0, int c = 0, int d = 0, int e = 0)
{
float sum = a + b + c + d + e; //求出总和
return sum / number;
}
int main()
{
float aver = average(3, 1, 5, 4);
cout << aver << endl;
return 0;
}
当然,用一个数组来存储数据并作为参数可以更加省事,但这只是个例子,为了引出今天的主角。
请主角出场之前,我们先来分析一下上面的方案的缺点。首先,它无法的参数的数量进行检测,无法检测到参数过多的情况;其次,上述函数只能计算不超过5个整型数字的平均值。而且,当average函数中b,c,d,e没有以缺省参数的形式定义的时候,主函数中的调用行为是未定义的。这样,average函数的第一个参数可能与number对应,也可能与a对应。
2.stdarg宏
首先声明一个函数(以求平均数的函数为样例,以便和缺省参数版本比较)声明方式:
float average(int number, ...);
注意这个省略号,它代表了该函数的可变参数列表。要访问它,我们需要先来了解一下stdarg宏。
头文件中包含了一个类型va_list和三个宏va_start,va_arg和va_end。在这个函数中,我们先要声明一个va_list型变量。声明方式:
va_list next; //va_list 变量名;
接着初始化这个变量。通过调用va_start来初始化这个变量,它的第一个参数是va_list变量的名字,第二个参数是省略号前最后一个有名字的参数(的名字)。初始化方式:
va_start(next, number);
初始化的过程就是把next变量设置为指向可变参数部分的第一个参数。
为了访问可变参数,还需要使用va_arg。这个宏接受两个参数:va_list变量和参数列表中当前参数的类型。va_arg可以返回va_list型参数当前指向的参数的值,并让va_list型参数指向可变参数列表中的下一个参数。va_arg使用方式:
float sum = 0;
for(int i = 0; i < number; i++)
sum += va_arg(next, int); //next只是一个va_list型变量的名字,并没有特殊意义
为实现可变参数累加,先定义一个浮点型的变量sum并初始化为0,再在每次访问可变参数时让sum加上当前参数并让next指向下一个参数。
最后,在函数末尾,还要用到va_end来“结束”这个va_list型变量,参数就是这个va_list型变量的名字。调用方式:
va_end(next);
最后,只需将计算得到的总和(sum)除以数据个数并返回,即可实现一个计算平均数的函数。
下面给出整个求平均数函数的定义过程:
float average(int number, ...)
{
//声明va_list型变量
va_list next;
//初始化next,使它指向可变参数列表中的第一个参数
va_start(next, number);
float sum = 0;
for(int i = 0; i < number; i++)
//va_arg返回当前(next指向的)参数并让next指向下一个参数
sum += va_arg(next, int);
//调用va_end以“结束”next
va_end(next);
return sum / number;
}
3.总结
4.警告
如果再va_arg中指定了错误的类型,那么结果是不可预测的。这个错误是很容易发生的,因为va_arg无法正确识别作用于可变参数之上的缺省参数类型提升。char、short和float类型的值实际上将作为int或double类型的值传递给函数。使用你在va_arg中使用后面这些类型时应该特别小心。