可变参数函数主要是利用了三个宏,va_start, va_arg, va_end和一个类型va_list。
先写个小例子,然后再说明这三个宏的含义。
例:计算指定数量的值的平均值
1 #include <stdarg.h> 2 3 float 4 average(int n_val, ...) 5 { 6 va_list arg; // 等价于char *arg; 7 int count; 8 float sum = 0; 9 10 va_start(arg, n_val); // arg = &n_val + 4 (假设这里int长度是4个字节) 11 12 for (count = 0; count < n_val; ++count) 13 { 14 sum += va_arg(arg, int); // 等价于sum += *arg; arg = arg + 4(假设这里va_arg第二个参数int类型的长度为4) 15 } 16 va_end(arg); // 等价于arg = 0; 17 18 return sum / n_val; 19 }
通过上面的例子,大体上知道了va_list其实就是typedef char* va_list;(PS:我发现大部分源代码在处理类似于指针什么的类型时,都用的是char,或者char*,或者unsigned char。不知道这个是为啥,比如atoi中)
然后是三个宏:
va_start:该宏初始化前面设置的va_list变量,把该va_list变量设置为可变参数中第一个参数的地址
源代码为:
#define va_start(ap, pN) ((ap) = ((va_list) (&pN) + __va_argsiz(pN))) // _va_argsiz宏是计算参数长度的
va_arg:源代码为
#define va_arg(ap, t) (((ap) = (ap) + __va_argsiz(t)), *((t*) (void*) ((ap) - __va_argsiz(t)))) // 一个逗号表达式。不理解干嘛先增加ap,然后又返回ap-增加的量。直接先返回ap的值,然后再增加ap不好吗?
va_end:源码
#define va_end(ap) ((void)0)
最后把strarg.h文件中说明_va_argsiz的源码给出来,自己不是很理解这里计算大小的过程
/* * Amount of space required in an argument list (ie. the stack) for an * argument of type t. */ #define __va_argsiz(t) \ (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
之前看SGI源码时,也发现有这样计算的:即先加上要除的内容,然后-1,然后再除。 不知道这样是为了确保什么,哪位知道,不妨告知。还有,这里为啥不直接是sizeof(t)完事,还麻烦的先除,然后又乘。