C语言中的可变参数列表
这次先用一个简单的求平均数的函数来说明可变参数列表,先放代码:
1 int my_average(int n, ...) 2 { 3 va_list arg; 4 va_start(arg, n); 5 int ret = 0; 6 int i; 7 for(i = 0; i < n; i++) 8 { 9 ret += (va_arg(arg, int)); 10 } 11 va_end(arg); 12 return ret / n; 13 } 14 15 int main() 16 { 17 int result = my_average(5, 1,2,3,4,5) 18 printf("%d\n", result); 19 return 0; 20 }
我们可以看到这个my_average(int n, ...)函数的参数部分,
n代表后面...可变参数的个数,
由于可变参数的限制,我们需要直接或间接的将参数的个数传递给函数。
函数中有以下几条语句来完成可变参数列表的使用:
1 va_list arg; va_start(arg, n); 2 va_arg(arg, int); va_end(arg);
接下来我们来逐个分析这些语句:
- va_list arg;
通过在IDE中转到定义,我们发现其定义为: typedef char* va_list; 也就是说,这里就是简单的创建了一个类型为char*的变量arg。
- va_start(arg, n);
我们通过转到定义,查看其定义:
#define __crt_va_start_a(ap, v) \
((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_start(ap, x) __crt_va_start_a(ap, x)
#define va_start __crt_va_start
不难发现va_start(arg, n);即相当于如下语句:
((void)(arg = (char*)_ADDRESSOF(n) + _INTSIZEOF(n)));其中_ADDRESSOF(v) 定义为:
#define _ADDRESSOF(v) (&(v))
即取v的地址
_INTSIZEOF(v)定义为:
#define _INTSIZEOF(n) \
((sizeof(n) + sizeof(int) -1) & ~(sizeof(int) - 1))
即当n小于4个字节时取4个字节,当n大于4个字节小于8个字节时取8个字节,以此类推。联系参数在栈中的存储顺序不难理解
va_start(arg, n);的作用就是获取可变参数列表中的第一个参数的地址。 - va_arg(arg, int);
va_arg的定义如下: #define __crt_va_arg(ap, t) \ (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) #define va_arg __crt_va_arg 其作用便是获取当前的参数,并将指针arg移动至下一个参数。 va_arg(arg, int); 即: (*(int*)((arg += 4) - 4)) 这里我一开始没看明白,后来明才知道这句写的多么有技巧。 首先让arg变量 加上4 ,然后取原来arg变量所指向的int值。 分开来看就比较清楚了,相当于这两句 (int*)arg += 4; *(int*)(arg - 4); 其所要表达的意思就是要获取arg当前所指向的int的值,并且还要让arg向后移动4个字节。 之前从没想过这两个语句可以一句搞定。
这里不太理解的是,为什么要让va_arg每获得一个参数便只能获取下一个参数,
为什么不在这个参数列表的最后添加一个固定的参数类似于'\0'的字符来判断参数的长度?
这样我们在使用的时候便不需要将参数的个数传递过去,岂不是更方便。 当然,才疏学浅,可能考虑不周,哪位大佬若是知道原因,还望指教。 - va_end(arg);
va_end定义如下: #define __crt_va_end(ap) ((void)(ap = (va_list)0)) #define va_end __crt_va_end 即简单的将arg指向空。

浙公网安备 33010602011771号