大多数时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数。但在某些情况下希望函数的参数个数可以根据需要确定。典型的例子有
大家熟悉的函数printf()、scanf()
可变参数的实现:
C语言头文件stdarg.h提供了一个数据类型va-list和三个宏(va-start、va-arg和va-end),va—start使vp指向第一个可选参数。va—arg返回参数列表中的当前参数并使vp指向参数列表中的下一个参数。va
—end把vp指针清为NULL。函数体内可以多次遍历这些参数,但是都必须以va—start开始,并以va—end结尾。用它们在被调用函数不知道参数个数和类型时对可变参数表进行测试,从而为访问可变参数提供
了方便且有效的方法。va-list是一个char类型的指针,当被调用函数使用一个可变参数时,它声明一个类型为va-list的变量,该变量用来指向va-arg和va-end所需信息的位置。
下面给出va_list在C中的源码:
typedef char * va_list;
采用ANSI标准形式时,参数个数可变的函数的原型声明是:
type funcname(type para1, type para2, ...)
这种形式至少需要一个普通的形式参数,后面的省略号不表示省略,而是函数原型的一部分,type是函数返回值和形式参数的类型。
调用者在实际调用参数个数可变的函数时,要通过一定的方法指明实际参数的个数,例如把最后一个参数置为空字符串。
下面给出一个具体的例子:
求n个数的和:
int Sum(int n,...) { int i = 0,sum = 0; va_list vp; va_start(vp,n); //va—start使vp指向第一个可选参数 for (i=0; i<n; i++) { sum +=va_arg(vp,int); } va_end(vp); //va—end把vp指针清为NULL。 return sum; }
printf函数实现:(此处只实现了基本类型的打印)
1 void my_print(const char *format,...) 2 { 3 char c = 0; 4 va_list vp; 5 va_start(vp,format); //vp指向第一个可选参数 6 while (*format) 7 { 8 c = *format; 9 switch (c) 10 { 11 case '%': 12 { 13 char cc = *(++format); 14 switch (cc) 15 { 16 case 'd': 17 { 18 char str[50]; 19 int n = va_arg(vp,int); //va—arg返回参数列表中的当前参数并使vp指向参数列表中的下一个参数。 20 char *string = _itoa(n,str,10); //把整数转为字符串 21 print_str(string); 22 } 23 break; 24 case 'f': 25 { 26 char str[50]; 27 double f = va_arg(vp,double);//va—arg返回参数列表中的当前参数并使vp指向参数列表中的下一个参数 28 char *string = _gcvt(f,10,str);//将浮点型数转换为字符串,取四舍五入 29 print_str(string); 30 } 31 break; 32 case 'c': 33 putchar(va_arg(vp,char)); 34 break; 35 case 's': 36 { 37 char *string = va_arg(vp,char*); 38 print_str(string); 39 } 40 break; 41 default: 42 break; 43 } 44 } 45 break; 46 default: 47 putchar(c); 48 break; 49 } 50 format++; 51 } 52 va_end(vp);
53 }
理解可变参数很重要的是要理解函数栈帧的创建与销毁,里面涉及到参数是如何压栈,这方面内容在前面的博客:http://www.cnblogs.com/zhonglongbo/p/8392026.html