可变参数列表源码的剖析

c语言有时会遇到一些参数可变的函数,如printf(),这些函数内部的参数列表是可变的。

printf()函数原型:

int printf(const char *format,...) 

printf()函数是以一个支持可变参数的函数,可以有多个参数,除了format以外,后面跟着的参数的个数和类型是可变的,用…作为一种占位符号。“…”称为可变参数列表,可以用来接受个数和类型不确定的参数。

可变参数列表在C语言中使用这样的三个宏(va_start,va_arg,va_end)和一个类型(va_list)来进行实现的,他们是定义在stdarg.h头文件中的。

1.源码剖析

(1)va_list

typedef char * va_list
定义了一个char*类型的va_list变量

(2)va_start

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
将v取地址,然后将其类型强转成va_list类型,也就是转换成一个char * 类型的指针,然后加上移动_INTSIZEOF(v)个字节的地址赋给ap


(3)va_arg

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
将指针ap指向当前位置移动_INTSIZEOF(t)个字节后的位置,然后取现在的ap所在位置的前 _INTSIZEOF(t)个字节的位置(ap移动前的原位置),并将其强制转换成t类型,然后对其解引用,取出这个地址的内容。

(4)va_end

#define va_end(ap)      ( ap = (va_list)0 )
将0强制转换成va_list即char *类型,赋给ap,即将ap置空

2.实例分析

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

#include<stdarg.h>


int average(int n, ...)
{
	va_list arg;//用于访问参数列表的未确定部分
	//	char * arg;

	va_start(arg, n);//用n的地址初始化arg. va_start的第一个参数是va_list的变量名,第二个参数是省略号前最后一个有名字的参数。初始过程把arg变量设置为指向可变参数部分第一个参数
	//	( arg = (char *)&n + 4);
	//#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )


	int i = 0;
	int sum = 0;

	for (i = 0; i < n; i++)
	{
		sum += va_arg(arg, int);//获取参数va_arg(va_arg list,type),一定要保证参数类型正确,va_arg返回这个参数的值,并且va_arg指向下一个可变参数
		//	sum += ( *(int *)((arg += 4) -4) );
		//	#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
	}

	va_end(arg);//清空arg
	//( arg = (char*)0 );
	//#define va_end(ap)      

	return sum / n;
}
int main()//函数功能:求取平均数
{
	int ave = average(3,  2, 3, 7);
	printf("%d\n",ave);
	system("pause");
	return 0;
}

3.可变参数的限制 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值