C++中函数的可变参数列表

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.总结

  1. 使用va_start首先得保证参数列表中至少要有一个命名参数。如果一个命名参数也没有,就无法使用va_start。
  2. 这些宏无法判断实际存在的参数的数量且无法判断每个参数的类型。
  3. 要回答上述两个问题,就必须使用命名参数。如上面的求平均数的函数,命名参数指定了实际传递的参数的数量;再如printf函数中的命名参数是格式字符串,它不仅指定了参数的数量,而且指定了参数的类型。

4.警告
如果再va_arg中指定了错误的类型,那么结果是不可预测的。这个错误是很容易发生的,因为va_arg无法正确识别作用于可变参数之上的缺省参数类型提升。char、short和float类型的值实际上将作为int或double类型的值传递给函数。使用你在va_arg中使用后面这些类型时应该特别小心。

你可能感兴趣的:(笔记)