我们以一个例子来引入可变参数列表,现在呢,我们需要写一个求平均值的函数,但是不知道求几个数的平均值,你给我几个数,我就求几个数的平均值
也就是说,我们得写出求任意个数的平均值的函数,那么如何设计这个函数呢?下面就来写一下:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int average(int n, ...)
{
va_list arg;
int sum = 0;
int i = 0;
va_start(arg, n);
for (i = 0; i < n; i++)
{
sum += va_arg(arg, int);
}
va_end(arg);
return sum / n;
}
int main()
{
int ret = average(3, 3, 4, 5);
printf("%d\n", ret);
system("pause");
return 0;
}
分析过程:
int average(int n, ...)
其中,n叫未知参数列表前第一个有名字的参数
比如,我们写个复杂的int average(int n,int m, ...),此时初始化arg的时候,arg的参数一定传的是m,因为它是跟后面的未知参数紧挨着,拿到m的地址才能找到后面参数列表的位置,所以这个地方的一定是.....前面第一个有名字的参数
va_list arg;
最终,我们拿arg来维护未知参数列表参数的位置
va_list是一个类型,而且是对char*类型的重定义,所以 va_list arg;又可以写成
char *arg;
int sum = 0;
int i = 0;
va_start(arg, n);
上面 va_start(arg, n);这句的意思是:初始化arg为未知参数列表的第一个参数的地址
(ap = (va_list)&v + _INTSIZEOF(v);这句话在预处理时替换为下面一行代码
(arg = (char*)&n + _INTSIZEOF(n)) _INTSIZEOF(n)是向上取整,取四个字节为一个整数,所以又可以简化如下:
(arg = (char*)&n + 4); //替换之后的结果
//#define va_start(ap,v) (ap=(va_list)&v+_INTSIZEOF(v))
for (i = 0; i < n; i++)
{
sum = va_arg(arg,int);
sum = va_arg(arg,int); 这句代码可以写成下面所示代码:
sum += (*(int*)((arg+= 4) - 4));//这里类型不能传错,如果传错类型,会出现不可预料的后果
//#define va_start(ap,t) (*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))
}
va_end(arg);
va_end(arg);是把arg赋值为空指针
(arg = (char*)0);
#define va_end(arg) (arg=va_list)0)
return sum / n;
}
int main()
{
int ret = average(3, 3, 4, 5);
printf("%d\n", ret);
+system("pause");
return 0;
}
注意:我们是用n来初始化arg,让arg初始化完之后,就是未知参数列表的第一个参数的地址,这里设计的很高效,取地址n,强制类型转换为char*,char*+=4,那么4又是怎么算出来的呢?4是根据int sizeof(n)算出来的,根据n的大小,+4正好跳过一个n,那就是要根据n的大小跳过去的,否则这个地方跳的不合理,va_arg(arg,int)是一个宏,它有两个参数,一个是arg,这是告诉我们,从arg位置开始取,取的类型为int
这段代码最关键的地方:就是(*(int*)((arg+= 4) - 4)),让arg向后走了,同时再-4,留下来的是它没加之前的地址。
当做完这些时,最后va_end(arg);把arg赋值为一个空指针,然后就停下来了
代码练习:求参数部分里面n个参数里面的最大值
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int Max(int n, ...)
{
va_list arg;//用来维护未知参数列表每个参数的地址
int max = 0;//这里的max不能赋值成0,赋成第一个值才比较合理
int i = 0;
va_start(arg, n);
max = va_arg(arg, int);//把第一个值取出来后,后面可以少取一次,不至于拿出第一个再和第一个比较
for (i = 1; i < n; i++)
{
int tmp = va_arg(arg, int);//使用tmp会避免它跳的太快
if (tmp>max)
{
max = tmp;//不要太过频繁的调用arg
}
}
return max;
}
int main()
{
int max = Max(3, 1, 2, 5);
printf("max=%d\n", max);
system("pause");
return 0;
}
再比如说:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int main()
{
printf("%d %c %s,100,bit,'q'");
system("pause");
return 0;
}
这个例子就说明,其实可变参数列表的第一个参数不一定是参数个数,像printf函数的第一个参数就不是表示参数的个数。