C语言的可变参函数
1.什么是可变参函数
可变参函数,顾其名而思义,就是该函数的参数数目不固定,例如我们的格式化输出函数
int printf(const char *fmt, ...)
这个函数的“…”参数就代表它是个可变参的函数。这个函数第一个参数一定是一个字符串,通过字符串中的“d%”,“%s”等等来对应后面的参数数量,这样以来就可以正确解析函数的参数。
2.可变参函数的原理
实现可变参函数,依赖于三个宏和一个数据类型
三个宏分别是va_arg, va_end, va_start
一个数据类型则是va_list
2.1 va_list类型
va_list 的 va是variable-argument(可变参数)。list则就是列表的意思,但它到底是什么呢?
typedef char *va_list;//Linux内核中va_list的实现
原来,va_list就是一个char *的数据类型。
2.2 va_start
va_start是一个宏定义,接收两个参数
#define va_start(ap, A) //Linux内核中宏定义的原型在此
(void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
第一个参数ap,就是刚才提到的va_list 类型,第二个参数A,就是一个确定的类型,也就是“…”的类型,它会将这些类型的地址放到va_list中,根据初始化A来初始化ap。
2.3 va_arg
va_arg是一个宏定义,接收两个参数
#define va_arg(ap, T) //Linux内核中宏定义的原型在此
(*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADN BND))))
ap 也是va_list类型, T是一个类型。从参数列表中取出一个参数,类型就是T
2.4va_end
va_end是一个宏
#define va_end(ap) (ap = (va_list) NULL)
通过这个宏,可以清楚的看到,将va_list类型的ap赋值为NULL。
2.5 这些宏怎么合作?
通过va_start初始化参数列表(得到具体的参数个数),参数列表由va_list定义,然后使用va_arg从参数列表中取出参数并处理,最后调用va_end来清理参数列表。
3. 可变参数函数的例子
格式化输出printf()函数在Linux内核中的源码:
int printf(const char *fmt, ...)
{
char printf_buf[1024];
va_list args; //定义参数列表args
int printed;
va_start(args, fmt); //用fmt初始化args
printed = vsprintf(printf_buf, fmt, args);//在vsprintf()中调用了va_arg的宏来处理每一个参数
va_end(args);//清理参数列表
puts(printf_buf);
return printed;
}
编写一个可变参的求和函数:
int sum(int cnt,...)
{
int sum=0;
int i;
va_list ap;//定义可变参数列表
va_start(ap,cnt);//用cnt初始化ap
for(i=0;i<cnt;++i)
sum+=va_arg(ap,int);//根据int类型从ap取出参数并处理
va_end(ap);//清理参数列表
return sum;
}
4. 可变参数函数的总结
- 可变参数函数必须有非可变的参数,并且位于可变参数的前面
- 编写可变参函数的人必须控制可变参的类型以及可变参的数目
- 可变参的个数必须通过非可变参数来获取