一,两种形式的可变参数
第一种的形式的头文件是<varargs.h>
第二种的形式的头文件是<stdarg.h>,是第一种的扩展。
区别:
- varargs.h比stdarg.h更有移植性,能够运行的系统平台也多一些,且更早。
- 如果要编写一个遵循ANSIC标准的程序,就必须使用stdarg.h。
二,varargs.h
<varargs.h>中有一些宏包括va_dcl,va_start,va_end以及va_arg。和va_list类型。va_alist一般由编程者来定义。
在函数定义的首部使用va_alist和va_dcl。
varargs.h经典实现
typedef char *va_list;
#define va_dcl int va_alist
#define va_start(list) list=(char*)&va_alist
#define va_end(list)
#define va_arg(list,mode) ((mode*)(list+=sizeof(mode)))[-1]
varargs.h和stdarg.h中的宏的区别就是va_start,前者只有一个参数就是va_list变量,后者多了一个变量就是最后一个固定参数。其他用法都是一样的。实现也差不多。
二,stdarg.h
stdarg.h这个头文件中包括一组宏和一个类型,此头文件是标准库的一部分。
va_list:可以定义一个变量,用来访问参数列表未确定的部分。其实va_list原型是typedef char* va_list;是一个char* 指针类型。所以这个类型定义出来的变量是存储参数地址的。结合参数类型,就能得出参数值。
va_start(va_list变量,最后一个固定参数):用来初始化va_list定义的变量,使va_list变量指向第一个可变参数。
va_arg(va_list变量,参数类型):返回这个参数的值,并使va_list变量指向下一个可变参数。
va_end(va_list变量):访问最后一个可变参数后,调用此宏。
注意:
底层c语言实现要求函数参数列表在内存中连续存储,只要知道当前参数地址,就能依次访问。
va_arg的第2个参数的类型不能为char ,short,float类型。char和short会转化成int,float会转化成double。
三,可变参数的限制
1,可变参数必须从头到尾逐个访问
2,参数列表中至少有一个确定的命名参数(对于stdarg.h来说)
3,可变参数宏无法知道可变参数的具体数目。需要人为指定
4,可变参数宏无法判断参数的类型。需要认为指定。
四,例子
varargs.h版本的error,printf
#include <stdio.h>
#include <varargs.h>
void error(va_alist) va_dcl
{
va_list ap;
char *format;
va_start(ap);
format=va_arg(ap,char*);
vfprintf(stderr,format,ap);
va_end(ap);
}
int printf(va_alist) va_dcl
{
va_list ap;
char* format;
int n;
va_start(ap);
format=va_arg(ap,char*);
n=vprintf(format,ap);
va_end(ap);
return n;
}
stdarg.h版本的error ,printf
#include <stdio.h>
#include <stdarg.h>
void error(char* format,...)
{
va_list ap;
va_start(ap,format);
vfprintf(stderr,format,ap);
va_end(ap);
}
int printf(char* format,...)
{
va_list ap;
int n;
va_start(ap,format);
n=vprintf(format,ap);
va_end(ap);
return n;
}
利用可变参数求和
#include <stdio.h>
#include <stdarg.h>
int sum(int values,...)
{
va_list ap;
va_start(ap,values);
int sum=0;
int i=0;
for(i=0;i<values;i++)
{
sum+=va_arg(ap,int)
}
va_end(ap);
return sum;
}