可变参数函数总结

可变参数函数主要是利用了三个宏,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)完事,还麻烦的先除,然后又乘。

原文地址:https://www.cnblogs.com/ziyoudefeng/p/2689095.html