C语⾔中函数可变参数解析
  ⼤多数时候,函数中形式参数的数⽬通常是确定的,在调⽤时要依次给出与形式参数对应的所有实际参数。但在某些情况下希望函数的参数个数可以根据需要确定。典型的例⼦有
⼤家熟悉的函数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函数实现:(此处只实现了基本类型的打印)
1void my_print(const char *format,...)
2 {
3char c = 0;
4    va_list vp;
5    va_start(vp,format); //vp指向第⼀个可选参数
6while (*format)
7    {
8        c = *format;
9switch (c)
10        {
11case'%':
12            {
13char cc = *(++format);
14switch (cc)
15                {
16case'd':
17                    {
18char str[50];
19int  n = va_arg(vp,int); //va—arg返回参数列表中的当前参数并使vp指向参数列表中的下⼀个参数。
20char *string = _itoa(n,str,10); //把整数转为字符串
21                        print_str(string);
22                    }
23break;
24case'f':
25                    {
26char str[50];
27double f = va_arg(vp,double);//va—arg返回参数列表中的当前参数并使vp指向参数列表中的下⼀个参数
28char *string = _gcvt(f,10,str);//将浮点型数转换为字符串,取四舍五⼊
29                        print_str(string);
30                    }
31break;
printf函数是如何实现的32case'c':
33                    putchar(va_arg(vp,char));
34break;
35case's':
36                    {
37char *string = va_arg(vp,char*);
38                        print_str(string);
39                    }
40break;
41default:
42break;
43                }
44            }
45break;
46default:
47            putchar(c);
48break;
49        }
50        format++;
51    }
52  va_end(vp);
53 }
理解可变参数很重要的是要理解函数栈帧的创建与销毁,⾥⾯涉及到参数是如何压栈,这⽅⾯内容在前⾯的博客: