我们在自己写一个函数时,我们发现:函数的传参参数是有限的,当你要传几个参数时,定义函数接收时就要定义几个形式参数。这个方法是十分麻烦的!
//举个例子:实现一个函数可以求任意个参数的平均值
#include<stdio.h>
int ave(int n,int i,int j,int k)//创建4个形参
{
int ave=0;
return ave=(i+j+k)/n;
}
int main()
{
printf("ave=%d\n",ave(3,3,4,5));//传4个参数
return 0;
}
那么有没有一种方法解决函数可以接受1个以上的任意多个参数(不固定)?
这就是可变参数列表!!!
#include<stdio.h>
int ave(int n,...)//"..."代表参数未知
{
va_list arg;
//typedef char * va_list;
int i=0;
int sum=0;
_crt_va_start(arg,n);
//#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//#define _crt_va_start(ap,v) ( ap = (va_list)( &(v) ) + _INTSIZEOF(v) )
for(i=0;i<n;i++)
{
sum+=_crt_va_arg(arg,int);
//#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
}
_crt_va_end(arg);
//#define _crt_va_end(ap) ( ap = (va_list)0 )
return sum/n;
}
int main()
{
printf("ave=%d\n",ave(3,3,4,5));
return 0;
}
上面这段代码用了4处宏,什么意思呢?
va_list arg : 创建一个字符指针arg,用于访问参数列表的未确定部分。
_crt_va_start(arg,n) : 初始化变量arg。第一个宏定义可以理解为求n类型的大小,由于定义为int型,故为4;第二个宏定义,我们把宏带入得到arg=(char *)(&arg)+4,把arg变量设置为指向可变参数部分的第一个参数。
_crt_va_arg(arg,int) :访问arg变量和参数列表中下一个参数的类型。
由于例子中可变参数都是整型,那么代码为 (int )((arg+=4)-4),表示返回这个参数的值,并自动指向下一个参数。_crt_va_end(arg) :当访问完毕最后一个可变参数之后,需要销毁指针变量arg,释放空间,相当于 arg=NULL。
当使用可变参数时也有一定限制:
可变参数必须从头到尾逐个访问。如果在访问几个参数后想半途终止,这是可以的;但是,如果不能一开始就访问列表中间的参数。
参数列表中至少有一个命名参数。
在代码中出现的宏是无法直接判断实际存在参数的个数及类型。
如果在指定未知参数,即_crt_va_arg第二个参数指定错了类型,那么由于指向的地址错误,其取出的值也是错误的,最后结果是不可预测的。
了解清楚代码意思后,做几个小练习
习题1:实现一个函数可以求任意个参数的平均值
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
double average(int n , ...)
{
va_list arg;
int i = 0;
double sum = 0;
_crt_va_start(arg, n);
for (i = 0; i < n; i++)
{
sum += _crt_va_arg(arg, double);
}
_crt_va_end(arg);
return sum / n;
}
int Average(int n, ...)
{
va_list arg;
int i = 0;
int sum = 0;
_crt_va_start(arg, n);
for (i = 0; i < n; i++)
{
sum += _crt_va_arg(arg, int);
}
_crt_va_end(arg);
return sum / n;
}
int main()
{
printf("average=%d\n", Average(3, 1, 2, 3));
printf("average=%lf\n", average(3, 1.5, 2.5, 5.0));
system("pause");
return 0;
}
习题2:实现一个函数可以比较任意个参数的大小
#include <stdio.h>
#include <stdlib.h>
int Max(int n, ...)
{
va_list arg;
int i = 0;
int max = 0;
int tmp = 0;
_crt_va_start(arg, n);
max = _crt_va_arg(arg, int);
for (i = 1; i < n; i++)
{
tmp = _crt_va_arg(arg, int);
max = max>tmp ? max : tmp;
}
_crt_va_end(arg);
return max;
}
double MAX(int n, ...)
{
va_list arg;
int i = 0;
double max = 0;
double tmp = 0;
_crt_va_start(arg, n);
max = _crt_va_arg(arg, double);
for (i = 1; i < n; i++)
{
tmp = _crt_va_arg(arg, double);
max = max>tmp ? max : tmp;
}
_crt_va_end(arg);
return max;
}
int main()
{
printf("max=%d\n", Max(3, 1, 7, 3));
printf("max=%lf\n", MAX(3, 2.0, 4.0, 6.0));
system("pause");
return 0;
}
习题3://模拟实现printf函数,能完成下面函数的调用。 print(“s ccc d.\n”,”hello”,’b’,’i’,’t’,100);
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int print_num(int n) //使用递归打印整型数
{
if (n > 9)
print_num(n / 10);
return putchar(n % 10 + '0'); //1+'0'='1'
}
void print(char *format, ...)
{
va_list arg;
char *p = NULL;
assert(format != NULL); //断言指针变量format不为空指针
_crt_va_start(arg, format);
while (*format != '\0')
{
if (*format == 's')
{
p = (_crt_va_arg(arg, char *));
while (*p)
putchar(*p++);
}
else if (*format == 'c')
putchar(_crt_va_arg(arg,char));
else if (*format == 'd')
{
int num=(_crt_va_arg(arg, int));
print_num(num);
}
else
putchar(*format);
format++;
}
_crt_va_end(arg);
}
int main()
{
print("s ccccc , d.\n", "hello", 'C', 'h', 'i','n','a', 2018);
system("pause");
return 0;
}