本章前面讨论了可变宏,该宏接受可变个数的参数。
头文件stdarg.h为函数提供了类似的能力。不过使用方法稍微复杂一些。必须按如下步骤进行:
1、在函数原型中使用省略号;
2、在函数定义中创建一个va_list类型的变量;
3、用宏将该变量初始化为一个参数列表;
4、用宏该问这个参数列表;
5、用宏完成清理工作。
现在详细讨论这些步骤。这类函数的原型应具有一个参量列表,参量列表中至少有一个后跟省略号的参量:
void f1(int n,...); //合法
int f2(int k,const char *s,...) //合法
char f3(char c1,...,char c2) //无效,省略号不是最后一个参量
double f3( ); //无效,没有任何参量
最右边的参量(省略号前)起着特殊作用;ANSI 使用parmN表示该参量。前例中,第一种情况下parmN为n,第二种情况下parmNo为k。传递给该参量的实际参数值将是省略号部分代表的参数个数。
例如,前面的f1()函数原型可以这样使用:
f1(2,200,400); //2个额外的参数
f1(4,13,117,18,23); //4个额外的参数
接下来,在头文件stdarg.h中声明的va_list类型代表一种数据对象,该数据对象用于存放参量列表中省略号部分代表的参量。可变函数定义的起始部分应像下面这样:
double sum(int lim,...)
{
va_list ap; //声明用于存放参数的变量
本例中,lim为参量parmN,由它来指定可变参数列表中的参数个数。
然后,函数将使用stdarg.h中定义的宏va_start( )把参数列表复制到va_list变量中。宏va_start()有两个参数:va_list类型的变量和参量parmN。我们接着前一个例子讨论,va_list类型的变量为ap,参量parmN为lim,因此,对va_start()的调用应如下所示:
va_start(ap,lim); //把ap初始化为参数列表
下一步是访问参数列表中的内容。这一步涉及宏va_arg()的使用。该宏接受两个参数:一个va_list类型的变量,一个类型名。
第一次调用va_arg()时,它返回参数列表的第一项,下次调用返回第二项,依此类推。类型参数指定返回值的类型。例如,如果参数列表中第一个参数为double类型,第二个为int类型,那么可使用如下语句:
double tic;
int toc;
...
tic = va_arg(ap,double); //取得第一个参数
toc = va_arg(ap,int); //取得第二个参数
注意,实际参数的类型必须与说明的类型相匹配。如果第一个参数是10.0,那么前面的tic部分的代码正常工作,但是如果参数是10,代码就可能无法工作。这里不会像赋值过程那样进行double到int的自动转换。
最后,应使用va_end()完成清理工作。例如,释放动态分配的用于存放参数的内存。该宏接受一个va_list变量作为参数:
va_end(ap); //清理工作
此后,只有用va_start()重新对ap初始化后,才能使用变量ap。
因为va_arg()不提供后退回先前参数的方法,所以保存va_list变量的副本是会有用的。C99专门为此添加了宏va_copy()。该宏的两个参数为va_list类型变量,它将第二个参数复制到第一个参数中:
va_list ap;
va_list apcopy;
double
double tic;
int toc;
...
va_start(ap,lim); //把ap初始化为参数列表
va_copy(apcopy,ap);//apcopy是ap的一个副本
tic = va_arg(ap,double); //取得第一个参数
toc = va_arg(ap,int); //取得第二个参数
此时,虽然从ap中删除了前两项,但还可以从apcopy中重新获取这两项。
程序清单16.18的简短示例程序说明了创建一个这样的函数的方法,该函数对可变参数进行求和运算。sum()的第一个参数是要进行求和运算的项目的个数。
程序清单16.18 varargs.c 程序
//varargs.c --使用可变个数的参数
#include <stdio.h>
#include <stdarg.h>
double sum(int ,...);
int main()
{
double s,t;
s = sum(3,1.1,2.5,13.3);
t = sum(6,1.1,2.1,13.1,4.1,5.1,6.1);
printf("return value for "
"sum(3,1.1,2.5,13.3): %g\n",s);
printf("return value for "
"sum(6,1.1,2.1,13.1,4.1,5.1,6.1): %g\n",t);
return 0;
}
double sum(int lim,...)
{
va_list ap; //声明用于存放参数的变量
double tot=0;
int i;
va_start(ap,lim);//把ap初始化为参数列表
for(i=0;i<lim;i++)
tot+=va_arg(ap,double);//访问参数列表中的每一项
va_end(ap); //清理工作
return tot;
}
输出如下:
return value for sum(3,1.1,2.5,13.3): 16.9
return value for sum(6,1.1,2.1,13.1,4.1,5.1,6.1): 31.6
可以发现,第一次调用sum()时对3个数求和,第二次调用时对6个数求和。
总之,可变函数的用法比可变宏更复杂,但是函数的应用范围更广。