可变参数也称为多参函数。如果听说过可变宏的童鞋可能更好理解这个概念,该宏接受可变个数的参数。而头文件stdarg.h为函数提供了类似的能力。不过使用方法较之可变宏稍微复杂一些。同时也相对的有一些使用要求
1.在函数原形中必须使用省略号XXXX(xx,...).
2.在函数定义中创建一个va_list类型的变量。
3.用宏将该变量初始化为一个参数列表。
4.用宏访问这个参数列表。
5.用宏对其进行清理操作。
上述的步骤可能我们列出来还不能够很好地理解。接下来我们就使用一个例子来进行说明。众所周知的printf函数的实现就是使用可变参数来进行实现的。所以我们也可以自己来写一个printf来实现该函数同时使我们对其进行理解。
打开msdn我们查找一下printf
从中我们可以看出 它的确是一个多参的函数。
现在我们来详细的根据我们上面讨论过的这些步骤来进行实现。
这类函数的原形应具有一个参量列表,参量列表中至少有一个后跟省略号二队参量。例如void f1(int n,...)
没有省略号或者是空的话编译器一定是会报错的。因为最右边的省略号起着特殊的作用,在ANSI标准中使用parmN表示该参量。传递给该参量的实际参数值将是省略号部分代表的参数个数。上面那个例子中参数就为n.而我们在模拟printf函数时我们需要引进多个参量,而这个参量的多少就需要根据你自己的情况而设计。
void myprintf(char*num,...)
接下来,在头文件stdargs.h中声明的va_list类型代表一种数据类型简单来讲我们需要引用这个数据对象来存放我们参量列表中省略号部分代表的参量。如
void myprintf(char*fmt,...)
{
va_list num;
本例中,num为参量parmN,由它来指定可变参数列表中的参数个数,也就是我们在printf中所要使用到的数据类型。在这我么需要定义一个值用来保存一个指针,这个我们在下面会用到。
然后,函数将使用stdargs.h中定义的宏va_start()把参数列表复制到va_list变量中。在宏va_start中有两个参数:va_list的变量和参量parmN。我们接着上面的 va_list类型的变量名为x,参量pamN为lim,因此,对va_start()的调用如下所示:
void myprintf(char*fmt,...)
{
va_list num;
char c;
va_start(num,fmt);
下一步我们需要访问参数列表中的内容。这一步我们需要使用对va_arg()的使用。该宏接受两个参数:一个va_list类型的变量和一个类型名。
第一次调用va_arg()时,它返回参数列表的第一项,下次调用时返回第二项,以此类推。类型参数指定返回的类型。如果指定类型和实际参数类型不同,代码可能就无法正常工作。但是我们在模拟使用printf函数的时候我们需要根据前面的类型符号来判断输出什么类型 比如我们在使用的时候会发现%d对应的是整数,为了能够较为完整地输出我们想要的函数。我们需要在这里使用switch语句来让我们在使用函数的时候能够进行自主选择同时我们需要使用上面的指针来进行移位操作来判定该字符是否使我们需要输出的关键字,不是的话按照printf来讲需要直接输出。这就是上面我定义char c。这样我们就可以列出这样的代码:
void myprintf(char*fmt,...)
{
va_list num;
char c;
va_start(num,fmt);
do
{
c=*fmt;
if(c!='%')
{
putchar(c);
}
else
{
switch(*++fmt)
{
case 'd':
printf("%d",*((int*)pArg));
break;
case 'f':
printf("%f",*((float*)pArg));
break;
default:
break;
}
va_arg(num,int);
}
++fmt;
}while(*fmt!='\0');
最后我们需要使用宏va_end()完成清理工作。例如,释放动态分配的用于存放参数的内存。该宏接受一个va_list变量作为参数:
void myprintf(char*fmt,...)
{
va_list pArg;
char c;
va_start(pArg,fmt);
do
{
c=*fmt;
if(c!='%')
{
putchar(c); //照原样输出字符
}
else
{
switch(*++fmt)
{
case 'd':
printf("%d",*((int*)pArg));
break;
case 'f':
printf("%f",*((float*)pArg));
break;
default:
break;
}
}
++fmt;
}while(*fmt!='\0');
va_end(num);
return;
}