1、省略号(ellipsis)
在无法给出所有传递给函数的参数的类型和数目时,可以使用省略号(...)指定函数参数表。有如下几种形式:
void fun1(int a, double b, ...); //给出确定的几个参数,其他用省略号
void fun2(int a ...); //省略号前有或者没有逗号都是可以的
void fun3(...); //也可以不确定任何参数,但和没有参数是不一样的
最典型的应用就是 printf 函数,printf 的声明和调用方法如下:
int printf( const char *format [,argument]... ); //官方声明
printf("My name is %s, age %d.", "AnnieKim", 24); //调用
2、通用的工作原理
大多数带有变长参数的函数都利用显式声明的参数中的一些信息,来获取调用中提供的其他可选实参的类型和数目。
比如 printf 函数,就是根据第一个参数推导可选实参:如果第一个'%'后有一个's',说明后面要有第二个参数,类型是字符串;如果还有第二个'%',后面跟一个'd',说明还需要第三个参数,是一个整型等等。
所以说,通常情况下,第一个参数是必不可少的。
3、如何获取变长参数
为了解决变长参数问题,需要用到以下几个宏(以下定义来自 MSDN),并且使用这几个宏时必须至少提供一个显式的参数:
#include <stdarg.h>
type va_arg(
va_list arg_ptr,
type
);
void va_end(
va_list arg_ptr
);
void va_start(
va_list arg_ptr,
prev_param
);
其中,type 是指要获取的参数的类型,比如 int,char * 等,arg_ptr 是指向参数列表的指针(va_list类型),prev_param 是指最后一个显式声明的参数,以用来获取第一个变长参数的位置。
使用步骤:
a)定义一个 va_list 类型的变量,变量是指向参数的指针。
b)va_start 初始化刚定义的变量,第二个参数是最后一个显式声明的参数。
c)va_arg 返回变长参数的值,第二个参数是该变长参数的类型。
d)va_end 将 a) 定义的变量重置为 NULL,即:释放内存。
注意事项:
a)变长参数的类型和数目不能通过宏来获取,只能通过自己写程序控制。
b)编译器对变长参数函数的原型检查不够严格,会影响代码质量。
4、举个例子
最后举个例子,是自己写的 printf 函数,只能用于处理'%s'和'%d'。为简单起见,没有做任何异常处理,理解这些宏的使用方法即可。
#include <iostream>
#include <stdarg.h>
using namespace std;
void myprintf(const char *format...)
{
va_list argptr;
va_start(argptr, format); //va_start
char ch;
while (ch = *(format++)) //逐个遍历format字符串
{
if (ch == '%') {
ch = *(format++);
if (ch == 's') {
char *name = va_arg(argptr, char *); //va_arg
cout << name;
} else if (ch == 'd') {
int age = va_arg(argptr, int); //va_arg
cout << age;
}
} else {
cout << ch;
}
}
cout << endl;
va_end(argptr); //va_end
}
int main()
{
myprintf("My name is %s, age %d.", "AnnieKim", 24);
return 0;
}
(SAW:Game Over!)