1.可变参数函数
可变参数函数,顾名思义,即函数的参数个数是可变的,例如 printf 的函数原型是:
int printf ( const char * format, ... );
可变参数函数的声明形式基本类似:只是在参数列表最尾部有一个参数 format 和 一个可变参数标志(三个点“…”)
2.相关的宏
在实现可变参数函数时,通常会用到以下几个宏:
void va_start(va_list ap, argN);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
使用这些宏需要包含头文件 stdarg.h。
在上述宏中:
va_list:用于遍历可变参数列表的变量类型
va_start:初始化 va_list 类型的对象 ap,使其指向调用 va_arg 之前的参数列表起始处
va_arg:返回 ap 指向的参数列表中的下一个参数,且每次调用 va_arg 都会修改 ap 使其指向下一参数,使得参数能依序返回
va_end:用于清理工作,使 ap 失效
va_copy:用于拷贝 va_list 的内容
在操作可变参数列表之前,必须先调用宏 va_start 或者 va_copy。
3.一个例子
以下是 execl 的一个可能的实现:
#include <stdarg.h>
#define MAXARGS 31
/*
* execl is called by
* execl(file, arg1, arg2, ..., (char *)(0));
*/
int execl(const char *file, const char *args, ...)
{
va_list ap;
char *array[MAXARGS +1];
int argno = 0;
va_start(ap, args);
while (args != 0 && argno < MAXARGS)
{
array[argno++] = args;
args = va_arg(ap, const char *);
}
array[argno] = (char *) 0;
va_end(ap);
return execv(file, array);
}
在上述实现中,使用一个空指针来标志参数列表的末尾,而在其他一些函数中,可以使用格式化字符串来明确参数个数或者在尾端加上一个特定的参数作为标志。
4.注意事项
- 调用变参函数时,传递的变参数目应不少于该函数所期望的变参数目(该数目由主调函数实参指定或由变参函数内部实现决定),否则会访问到函数参数以外的堆栈区域,可能导致堆栈错误。
- va_arg(ap, type) 宏获取变参时,type 不可指定为以下类型,原因与默认参数提升有关:
- char、signed char、unsigned char
- short、unsigned short
- signed short、short int、signed short int、unsigned short int
- float