可变参数函数就是输入的参数个数是可变的
最经典的是printf和scanf,这两个函数的声明如下:
1 int printf(const char *format, ...);
2 int scanf(const char *format, ...);
这两个函数声明中省略号(...)表示任意个数的参数
函数的数据是存放于栈中的,那么给一个函数传递传递参数的过程就是将函数的参数从右向左逐次压栈,例如:
func(int i, char c, doube d)
这个函数传递参数的过程就是将d,c,i逐次压到函数的栈中,由于栈是从高地址向低地址扩展的,所以d的地址最高,i的地址最低。
典型的写法如下:
1 void func(char *fmt, ...){
2 va_list ap;
3 va_start(ap, fmt);
4 va_arg(ap, int);
5 va_end(va);
6 }
这里ap是一个指针,指向参数的地址。
va_start()让ap指向函数的最后一个确定的参数(声明程序中是fmt)的地址。
va_arg()根据ap指向的地址和第二个参数所确定的类型,将这个参数的中的数据提取出来,作为返回值,同时让ap指向下一个参数。
va_end()让ap这个指针指向0。
关于这三个参数实现的宏可以参看下面的实现:
1 #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
2 #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //第一个可选参数地址
3 #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一个参数的值
4 #define va_end(ap) ( ap = (va_list)0 ) // 将指针置为无效
以下为编译PL0语言的SET.c源码中的createset()函数,在此函数中就是用了可变长参数:
1 void setinsert(symset s, int elem)
2 {
3 snode* p = s;
4 snode* q;
5
6 while (p->next && p->next->elem < elem)
7 {
8 p = p->next;
9 }
10
11 q = (snode*) malloc(sizeof(snode));
12 q->elem = elem;
13 q->next = p->next;
14 p->next = q;
15 } // setinsert
16
17 symset createset(int elem, .../* SYM_NULL */)
18 {
19 va_list list;
20 symset s;
21
22 s = (snode*) malloc(sizeof(snode));
23 s->next = NULL;
24
25 va_start(list, elem);
26 while (elem)
27 {
28 setinsert(s, elem);
29 elem = va_arg(list, int);
30 }
31 va_end(list);
32 return s;
33 } // createset