今天在查Linux C函数库时候,一下子翻到了附录,于是看到了不定参数几个字。感觉挺有意思的,而且以前也看到很多不定参数的函数的原型,当时很是不解,很惊叹在不知道函数参数和个数的情况下还能使用。因为时间问题当时也没去了解,现在是该了解的时候了。
其实我们在写C程序的时候,就不知不觉的用到了不定参数,只是不知道的而已。这个函数就是printf()!
这个函数的原型是:
int printf(const char *format[, argument]...);
这个函数的返回类型是int,当它不输出任何字符时返回0。(printf("");)
在format后面的参数不但不定个数,而且类型也是不定的。
这些参数是one by one 的堆 stack 里的,我觉得这个很关键!
那么printf()函数是如何正确的取出这些写参数的呢?
秘诀就在format,函数根据format里的格式("%d %f...")依次把堆在栈里的参数取出来。而这取出的动作就是用到va_arg, va_end, va_start這三個macro再加上va_list。
不定参数相关的表头文件是stdarg.h,在这个头文件中关于以上三个宏和一个变量的定义如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
typedef char * va_list;
#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 )
首先解释一下宏定义_INTSIZEOF(n),使用macro是为了跨平台时能保持程序运行正确。若传了一个2bytes的不定参数,但在一个32位的平台如堆栈就会是4个bytes,而不是2个bytes,知道意思就好,可能这个例子不太好,所以参数的取出就要非常小心。使用_INTSIZEOF(n)在于保证指针移动正确。这也是预处理的目的之一。
接着解释后面的三个宏,va_list其实就是char *类型的数据,大家都了解吧。
va_start(ap,v);ap的类型就是va_list,v是第一个参数,(va_list)&v得到的是第一个参数的地址,而va_start(ap,v)的到的ap指向的是第二个参数的地址,有人说是第一个参数的地址,我认为是不对的,大家可以做个试验就知道了。不定参函数都是直接给出第一个参数的,他在调用的时候是直接通过这个给出的参数调用的,而不是通过参数指针ap调用的,因为它ap开始处指的就是第二个参数。调用宏va_arg(ap,t)后ap有指向了下一个参数,但这个宏返回的ap指向的上一个参数的指针,仔细分析一下括号就知道了。t是类型名称,可以是int,char等。这个里面还用到了强制类型转换。参数结束的条件由程序自己设定,通常以某个特定的参数表示参数列表结束,用作循环的结束条件。最后va_edn(ap),将ap归零。
下面有两个示例,可以试一试。
#include
#include
/*void fun(char *s,...)
{
va_list ap;
int t;
va_start(ap,s);
printf("%s",s);
while ( (t = va_arg(ap, int)) )
printf("%d",t);
va_end(ap);
}
void main()
{
int a = 1, b = 2, c = 3;
fun("test:",a,b,c,NULL);
printf("\n");
}
--------------------------------------------------------------------
#include
#include
int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
printf("marker is : %d\n", *marker);
while( i != -1 )
{
printf("The sequence of i is: %d\n",i);
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
printf("%d,%d\n", sum, count);//打印总和,输入的数字个数
return( sum ? (sum / count) : 0 );
}
void main()
{
int avge;
avge = average(3,4,5,6,-1);
printf("The average is : %d\n", avge);
}