可变参数列表解析

有的时候你有没有想过一个函数可以实现处理可变个参数呢?也就是说你可以传一个参数,也可以传两个参数,也可以更多,那么要如何来实现呢?

那我们先来看这样一个例子:

这个例子是用一个函数来实现求任意个参数的平均值的

#include<stdio.h>
#include<stdarg.h>
int average(int n,...)
{
	va_list arg;
	int i = 0;
	int sum = 0;
	va_start(arg,n);
	for(i=0;i<n;i++)
	{
		sum += va_arg(arg,int);
	}
	return sum/n;
	va_end(arg);
}
int main()
{
	int a = 1;
	int b = 2;
	int c = 3;
	int avg1 = average(2,a,c);
	int avg2 = average(3,a,b,c);
	printf("avg1 = %d\n",avg1);
	printf("avg2 = %d\n",avg2);
	return 0;
}

执行的结果也很简单,但是这个实现的方法却和我们平时求平均值的方法不太一样,那么接下来我们就一起来分析一下吧。

 

  • 声明一个va_list 类型的变量arg,它用于访问参数列表的未确定部分。
  • 这个变量是调用va_start 来初始化的。它的第一个参数是va_list的变量名,第二个参数是省略号前最后一个有名字的参数。初始化过程是把arg变量设置为指向可变参数部分的第一个参数。
  • 为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数的访问类型。在这个例子中所有的可变参数都是整型。va_arg返回这个参数的值,并使用va_arg指向下一个可变参数。
  • 最后,当访问完毕最后一个可变参数之后,我们需要调用va_end。

以上就是可变参数的使用方法。

但同时也要注意可变参数也是有限制

 

  • 可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
  • 参数列表中至少由一个命名参数。如果连一个命名参数都没有,就无法使用va_start。
  • 这些宏是无法直接判断实际存在参数的数量。
  • 这些宏无法判断每个参数的是类型。
  • 如果在va_start中指定了错误的类型,那么其后果是不可预测的。

 

也许看到这里,你还是不能理解到底如何使用可变参数。

那么你就需要了解这样一个头文件

 

#include<stdarg.h>

 

这个头文件里包含了以下内容:

 

 

#typedef char * va_list;

#define _INTSIZEOF(n)  ((sizeof(n)  +sizeof(int)  -1)& ~(sizeof(int) -1))

#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 )

所以说看到这里,你也许就明白了,其实可变参数的实现过程是使用宏的封装。

当你需要使用可变参数时 ,你就使用这个头文件里已经有的宏吧,用宏来替换你的代码,但是还要注意它们的使用顺序。

这里再给大家一个例子:

 

#include<stdio.h>

#include<strarg.h>

int max(int n,...)

{

    va_list arg;

    int max = 0;
    
    int i = 0;

    va_start(arg,n);

    max = va_arg(arg,int);

    for(i=0;i<n;i++)

        {

            int tmp = va_arg(arg,int);

            if(tmp>max)

            {

              max = tmp;
            }

        }
    va_end(arg);
    return max;

}          

你看明白了么?

其实这个就是求任意个整数的最大值的函数哦!

那么其实printf函数内部也是通过这种可变参数来实现的,但是呢,比较复杂,这里,我们简单地实现一些printf的功能。

函数原型:print(char *format,...)

可以完成下面函数的调用:

print("s ccc d.\n","hello","b","i","t",100);

前面代表它的格式,s代表字符串输出,c代表字符输出,d代表整型输出,其他的直接打印就好。

实现方法如下:

#include<stdio.h>
#include<stdarg.h>
void print_int(int n)//打印整数
{
	if(n>9)
	{
		print_int(n/10);
	}
	putchar(n%10+'0');
}
void print(const char *format,...)
{
	va_list arg;
	va_start(arg,format);
	while(*format)
	{
		switch(*format)
		{
		case's':
			{
				char *str = va_arg(arg,char *);
				while(*str)
				{
					putchar(*str);
					str++;
				}
			}
			break;
		case'c':
			{
				char ch = va_arg(arg,char);
				putchar(ch);
			}
			break;
		case'd':
			{
				int ret = va_arg(arg,int);
				print_int(ret);
			}
			break;
		default:
			putchar(*format);
			break;
		}
		format++;
	}
	
	
}
int main()
{
	print("s ccc d.\n","hello",'b','i','t',100);
	return 0;
}

怎么样?仔细读一读,是不是还挺简单的?

那么,你学会了么?

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值