可变参数原理

在函数的定义中,列出了函数期望接受的参数,但原型只能显示固定数目的参数,让一个函数在不同的时候接受不同数目的参数是不是可以呢?
比如一个求两数的最大值函数 int Max(int m,int n)
{
return m>n?m:n;
}
求三个数的最大值时,可以通过主函数调用 Max函数嵌套实现功能,但当四个,五个,六个值,求
最大值时,我们再去调用它时就会让代码变得冗杂,降低效率。如果函数的参数能够随着需求增大或减小,也就是可变,那将会减少比必要的麻烦。
其实在C语言编程中会遇到一些参数个数可变的函数,例如printf()
这个函数,它的定义是这样的:
int printf( const char* format, …);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是
可变的,例如我们可以有以下不同的调用方法:
printf("%d",i);
printf("%s",s);
printf("%d,%s", i, s);
可见printf的功能是极其强大的,既然那么强大
不妨让我们去底层看一看它究竟是什么东西吧
在这里插入图片描述
打扰了。。。。。。。。。

虽然底层复杂,但是还是发现里面几个特别的函数的,(可变参数)的英文为variable-argument
那是不是跟va_list,va_start这几个函数有关?
在这里插入图片描述
查下资料。。原来程序设计中,允许这样去调用函数void print(int cnt,…),要求省略号的所有形参数据类型必须一致,即函数的参数列表有多少个形参在调用之前并不知情。
但是,根据函数形参入栈的规律(都是相邻压进栈的)和每个形参的类型都一样,我们可以通过偏移地址算出当前形参的地址。因此,这种调用也是合法的。
具体实现在C库中提供了一组宏:va_list、va_start、va_arg、va_end。

先来看几个设计变参函数要用到的几个宏,这几个宏定义在stdarg.h文件中。
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 )
1)va_list arg;//定义一个va_list类型变量(指针类型),变量名arg
2)va_start用来初始化ap(va_list型),从该宏的内容可看出ap指向了v后面的第一个参数,通常调用此宏使ap指向第一个变参。
3)va_arg是将ap指向下一个参数,t为类型,该表达式返回下一个参数的值。
4)va_end是将ap置0,释放指针空间

下面我们写一个简单的可变参数的函数,求指定数量的平均值.
#include
#include

float average(int n,…)
{
va_list var_arg;
int count;
float sum=0;
//准备访问可变参数
va_start(var_arg,n);
//添加取自可变参数列表的值
for(count=0;count
{
sum +=va_arg(var_arg,int);
}
va_end( var_arg );
return sum /n;
}
int main()
{
printf("%f\n",average(2,20,30));
printf("%f\n",average(3,20,15,41));
printf("%f\n",average(5,20,33,10,14,20));
return 0;
}
在这里插入图片描述
既然可变参数的几个函数我们弄清楚了,不如我们自己写一个打印函数吧。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190727172657304.png
首先我们得判断将要输出的是那种类型,%s->字符串,%c->字符,%d->整数,%f->浮点型

void print( char* start, …)
{
//思路:读取每一个字符然后 putchar
va_list arg;
_crt_va_start(arg, start);
assert(start);
char *wrold = start;
while (*wrold)
{
char *c = NULL;
if (*wrold == ‘%’)
{
wrold++;
switch (wrold)
{
case ‘c’ :
putchar(_crt_va_arg(arg, char));
break;
case ‘d’:
printf("%d", _crt_va_arg(arg, int));
break;
case ‘s’:
c = _crt_va_arg(arg, char
);
while (*c)
{
putchar(*c++);
}
break;
}
}
else
{
putchar(*wrold);
}
wrold++;
}
_crt_va_end(arg);

}

int main()
{
print(“abc%czzz\n”,‘x’);
print(“abc%dzzz\n”,49);
print(“abc%szzz\n”,“xingzengdao”);
return 0;
}
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值