注:所使用环境为VS2015
在刚开始学习C语言的时候,一般写的代码每个函数的参数肯定都是给定的,给定两个实参,那在函数调用后那就只能使用者两个参数,如果二者的数目不统一那么编译器就会报错。但是如果使用了可变参数那么就不需要担心这些,而且够以一种良好定义的方法访问数量未定的参数列表。接下来就给出多个数求平均值的实现来进行比较。
第一种:给定参数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <Windows.h>
int average(int n1, int n2, int n3, int n4)
{
return (n1 + n2 + n3 + n4) / 4;
}
int main()
{
int aver = average(2, 3, 4, 3);
printf("average = %d\n", aver);
system("pause");
return 0;
}
这就是我们最初经常写的代码,这样的代码当我们写形参时十分麻烦,如果在实参数目有所改变的时候,对应函数的形参也要更改,有更好的方法干嘛不用呢对吧~接下来就是可变参数的写法
第二种:可变参数(代码后面有详细的解释~)
要想使用可变参数必须调用<stdarg.h>头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <Windows.h>
#include <stdarg.h>
int average(int n, ...)
{
int sum = 0;
int avg = 0;
int i = 0;
va_list arg;
va_start(arg, n);
for (i = 0; i < n; i++)
{
sum += va_arg(arg, int);
}
avg = sum / n;
va_end(arg);
return avg;
}
int main()
{
int aver = average(4, 2, 3, 4, 3);
printf("average = %d\n", aver);
system("pause");
return 0;
}
先用图简单的解释下函数的可变参数写法:
我们没有见过的就是图中四个框中的内容,下面一个一个解释:
1:va_list arg;
在VS2015中我们可以按F12转到定义;所以我们就看看va_list的定义:
所以其实就是定义了一个char* 指针,至于名字可以自己添加;
2:va_start(arg,n);
第一次转定义后,跳到了下面的定义:
我们选定__crt_va_start再次跳转,
这才是他们的真面目!!!我们发现其实他们都是宏定义,宏定义在使用的过程中,直接将参数全部替换即可,
如果对宏不了解的,也可以看看另一篇博客 http://blog.youkuaiyun.com/sssssuuuuu666/article/details/69943616 ;下来我们就进行替换的操作。
其中的_ADDRESSOF作用就相当于取地址操作&,_INTSIZEOF相当于提升到一个int的大小,如果n是1~4那么结果就是4;5~8就是8;9~12就是12,以此类推;
所以这句话就可以说是将n的地址给char*,然后再加上_INTSIZEOF(n)的大小;这样说又会不明白加上有什么用,函数实参和形参在栈帧中的调用过程也是十分重要的,假设下面是高地址,上面是低地址,形参在栈中是先压最右边的参数,由于栈空间是从高地址向低地址写入,所以最上面的也就是n,我们知道了n的地址通过加地址就知道了在n下面的参数地址。大概用图画一下:
3:va_arg(arg, int):
有了前面的准备,接下来就是取参数的过程了,括号内的arg是自己定义的名字,还是和上面一样我们跳转到最内部的定义然后进行化简:
当我看到最后化简出的结果真的是!
又加4又减4是要干嘛!只能慢慢的再缕了。。
经过第二步我们知道现在char* arg里面已经是未知参数中的第一个参数的地址了,
(arg += 4)表示将arg加4也就是指向第二个元素,跳过第一个未知参数,此时(arg+=4)这个表达式的结果是第二个参数的地址,出了括号再减去4又得到了第一个未知参数的地址,此时arg已经指向了下一个参数,但是得到的却是第一个未知参数的地址,从而为下一次获取参数做准备。
4:va_end(arg);
va_end(arg)的作用就是将arg指针置为NULL,到此整个过程就完了~剩下的就都能看得懂了。
如有错误,请联系我谢谢~