如何实现带可变长参数的函数
关于如何实现带可变长参数的函数,先看这两篇文章。
https://www.ibm.com/developerworks/cn/linux/l-va/index.html
以及
统称带可变长参数的函数为VA函数,variable argument function。
1,声明
VA函数的原型声明包括两部分:个数确定的固定参数部分和个数不确定的可选参数部分。
type VAFunction(type arg1, type arg2, …);
其中可选参数部分由 … 表示。
2,思路简单描述
VA函数的实现简单说就是对参数指针的使用和控制。对于固定参数部分,可以直接从函数定义中获得;对于可选参数部分,先将指针指向第一个可选参数,然后依次后移指针,直到指针指向结束标志为止。因此VA函数必须先约定好结束标志。
3,VA的宏定义
C提供了一系列独立于硬件架构和硬件平台的宏用于实现VA函数,这些宏都定义在stdarg.h中。关键的几个如下:
1) va_list arg_prt;
va_list是指向char的指针,X86下va_list的定义如下:
typedef char* va_list;
arg_prt初始值应指向第一个可选参数。这是如何实现的呢?通过va_start(arg_ptr, argN)。
2) va_start(arg_ptr, argN);
va_start(arg_ptr, argN);的作用是使指针指向第一个可选参数。其中argN是位于第一个可选参数之前的固定参数(亦即最后一个固定参数,在…最前面)。如有意VA函数void va_test(char a, char b, char c, …),则它的固定参数依次是a,b,c,最后一个固定参数argN是c,因此就是va_start(arg_ptr, c)。
3) va_arg(arg_ptr, type);
确定第一个可变参数的位置之后,接下来要做的就是移动指针直至其指向结束标志。这通过va_arg(arg_ptr, type);实现。va_arg(arg_ptr, type)返回指针指向的参数,返回类型为type,并使指针指向参数列表中下一个参数。
4) va_end(arg_ptr);
清空参数列表,并置参数指针无效。每次调用va_start()/va_copy()后,应有相应的va_end()与之匹配。
其他VA宏说明可参考微软的文档。
上述的宏的实现如下:
. #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
. #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //第一个可选参数地址
. #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一个参数地址
. #define va_end(ap) ( ap = (va_list)0 )
详细解释参考IBM的文档。
4,举例
下面是一个计算任意个自然数平方的例子。
int SqSum(int n1, ...)
{
va_list arg_ptr; // 声明指针
int nSqSum = 0;
int n = n1; // 指定最后一个固定参数
va_start(arg_ptr, n1); // 指针指向第一个可选参数
while (n > 0) // 指定结束字符
{
nSqSum += (n * n);
n = va_arg(arg_ptr, int); // 返回参数列表中下一个可选参数,类型为int,并指向下一个参数
}
va_end(arg_ptr); // va_end()与va_start()对应
return nSqSum;
}
// 调用时
int nSqSum = SqSum(7, 2, 7, 11, -1);
5,利用VA函数写简洁的调试打印函数
调试时,如果直接使用printf()捉着fprintf(),调试结束后删除它们会非常麻烦。如果写成这样的宏:
#ifdef DEBUG
printf(…);
#endif
如果数量很多,代码看起来也很不好看。
可以考虑写一个VA函数,包住fprintf(),然后在此函数中引入一个全局变量,以控制是否输出调试信息。
例如:
void debug_write(char* fmt, …)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
6,顺便说一下vprintf系列函数
int vprintf / vscanf(const char * format, va_list ap); // 从标准输入/输出格式化字符串
int vfprintf / vfsacanf(FILE * stream, const char * format, va_list ap); // 从文件流
int vsprintf / vsscanf(char * s, const char * format, va_list ap); // 从字符串
posted on 2019-10-15 11:28 freshair_cn 阅读(512) 评论(0) 编辑 收藏 举报