近日在模式中进行非线性方程组求解时遇到变长参数函数的问题,以前从来没有自己写过变长参数的函数,于是补了一下课,将近日对该小问题的学习和理解整理如下。
一、变长参数函数(variadic function)[1,2]
其实我们在刚开始学习程序设计语言的时候就已经接触到变长参数函数,最明显的例子就是C中的printf函数,该函数的调用方式是printf(format,varibale_list),我们可以一次输出很多个变量,只要在format字符串中用%定义了这些变量的类型。再拿Wiki中的一段小程序[1]作为例子:
#include
double average(int count, ...)
{
va_list ap;
int j;
double tot =0;
va_start(ap, count);/* Requires the last fixed parameter (to get the address) */
for(j =0; j < count; j++)
tot +=va_arg(ap,double);/* Increments ap to the next argument. */
va_end(ap);
return tot / count;
}
这个函数的目的是求任意多个数的平均值,从这个例子中可以学习变长参数函数的使用方法:
1、采用#inculde 语句添加实现变参函数所需要的头文件
2、变长参数函数的声明和定义中用...表示变参列表
3、在函数内部用va_list关键字声明一个变参列表指针ap
4、用va_start将ap初始化为指向函数最后一个定参(在该例子中average函数的最后一个定参是count)处
5、用va_arg从变参列表中依次获取参数
6、用va_end将变参列表指针释放
那va_list类型以及va_start、va_arg、va_end到底是什么呢?答案是:宏。这些宏的定义位于stdarg.h(C)或者cstdarg.h(C++)中,包括上面提到的va_list类型以及va_start、va_end、va_arg和va_copy宏[3]。由于硬件平台和编译平台的不同[4],这些宏的定义和实现也有所差异。
二、GCC中的实现
在GCC的stdarg.h(/usr/lib/gcc/x86_64-redhat-linux/4.4.5/include)头文件中可以看到如下内容:
……
typedef __builtin_va_list __gnuc_va_list;
……
typedef __gnuc_va_list va_list;
……
#define va_start(v,l) __builtin_va_start(v,l)
#define va_end(v) __builtin_va_end(v)
#define va_arg(v,l) __builtin_va_arg(v,l)
#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L || defined(__GXX_EXPERIMENTAL_CXX0X__)
#define va_copy(d,s) __builtin_va_copy(d,s)
#endif
#define __va_copy(d,s) __builtin_va_copy(d,s)
这说明gcc在这个头文件中定义了va_list类型和va_start、va_end、va_arg、和va_copy几个宏。关于这些宏的实现,在gcc中没有再找到其他信息,因为上面的定义中将va系列宏指向了_builtin_函数,这些函数编译器能自动识别并做相应处理[5]。
三、VC中的实现
在VC中这些宏的实现方式有明确定义:typedef char * va_list;#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 )
#define _INTSIZEOF(n)这句体现了programmer的智慧,需要详细分析才能理解,可参考[6]。额,注意到没,貌似VC不支持va_copy喔[7]。
References:转载本文请联系原作者获取授权,同时请注明本文来自彭彬科学网博客。
链接地址:http://blog.sciencenet.cn/blog-430991-700026.html
上一篇:让linux挂载的移动硬盘具有执行权限
下一篇:Fortran学习笔记(3): 一些非常有用的Fortran函数库