· 变长参数(stdarg.h)
变长参数是c语言的特殊参数形式,例如如下函数声明:
int printf(const char * format,...);
如此的声明表明,printf函数除了第一个参数类型为const char*之外,其后可以追加任意数量、任意类型的参数。
在函数实现部分,可以使用stdarg.h里的多个宏来访问各个额外的参数:假设lastarg是变长参数函数的最后一个具名参数(printf里的format),那么在函数内容定义类型为va_list的变量:
va_list ap;
该变量以后将会依次指向各个可变参数,ap必须用宏va_start初始化一次,其中lastarg必须是函数的最后一个具名的参数。
va_start(ap,lastarg);
以后,可以使用va_arg宏来获取下一个不定参数(假设已知其类型为type):
type next = va_arg(ap, type);
在函数结束前,还必须用宏va_end来清理现场。
整理:
va_list 实际是一个指针,用来指向各个不定参数。由于类型不明,因此这个va_lish以void *或者char *为最佳选择;
va_start 将va_list定义的指针指向函数的最后一个参数后面的位置,这个位置就是第一个不定参数;
va_arg 获取当前不定参数的支,并根据当前不定参数的大小将指针指向下一个参数;
va_end 将指针清0。
按照以上思路,va系列宏的一个最简单的实现就可以得到如下:
#define va_list char* #define va_start (ap,arg) (ap = (va_list)&arg + sizeof(arg)) #define va_arg(ap,t) (*(t*)((ap+=sizeof(t) - sizeof(t))) #define va_end(ap) (ap = (va_list)0)
经验:
在很多时候,我们希望在定义宏的时候也能够像printf函数一样可以使用边长参数,即宏的参数可以是任意个,这个功能可以由编译器的边长参数宏来实现。在GCC编译器下,边长参数宏可以使用“##”宏字符串链接操作实现,比如:
#define printf(arg...) fprintf(stdout,##args)
而在MSVC下,可以通过__VA_ARGS__这个编译器内置宏,比如:
#define printf(...) fprintf(stdout,__VA_ARGS__)