【C语言】可变参数列表解析

C语言中的可变量参数是通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数(不固定)。

举个例子解析一下:

实现一个函数可以求任意个参数的平均值

源代码:

//实现一个函数可以求任意个参数的平均值

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 
#include 


int average(int n,...)
{
	va_list arg;
	int sum = 0;
	int i = 0;
	va_start(arg, n);
	for (i = 0; i < n; i++)
	{
		sum += va_arg(arg, int);
	}
	va_end(arg);
	return sum / n;
}

int main()
{
	int ret = average(3, 3, 4, 5);
	printf("%d\n", ret);
	ret = average(4, 3, 4, 5, 8);
	printf("%d\n", ret);
	system("pause");
	return 0;
}

下面逐句分析一下:

//实现一个函数可以求任意个参数的平均值

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 
#include 


int average(int n,...)//可变参数列表用...表示
{
	va_list arg;
	//typedef char *  va_list;为了区分,在这里将char*类型重定义为va_list,其实就是char*类型-->char* srg;
	int sum = 0;
	int i = 0;
	//初始化arg为未知参数列表的第一个参数的地址
	va_start(arg, n);
	//#define va_start _crt_va_start
	//#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
	//arg = ((va_list)_ADDRESSOF(n) + _INSIZEOF(n))
	//arg = ((char*)&n+4)-->找到n的位置,然后再找到未知参数的位置
	for (i = 0; i < n; i++)
	{
		sum += va_arg(arg, int);
        //#define va_arg _crt_va_arg
        //#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
		//sum += (*(int *)((arg += _INTSIZEOF(int)) - _INTSIZEOF(int)))
		//sum += (*(int *)((arg += 4) - 4))-->将arg地址改变为下一个数的地址,但是留下来的是此地址,最后强制类型转换将数加上去,依次循环
	}
	va_end(arg);
	//#define va_end _crt_va_end
	//#define _crt_va_end(ap)      ( ap = (va_list)0 )
	//arg = (char*)0;
	//arg始终维护的是未知参数列表中未知参数的位置,现在没用了,就赋值为空指针
	return sum / n;
}

int main()
{
	int ret = average(3, 3, 4, 5);
	printf("%d\n", ret);
	ret = average(4, 3, 4, 5, 8);
	printf("%d\n", ret);
	system("pause");
	return 0;
}
  • 声明一个va_.list类型的变量arg,它用于访问参数列表的未确定部分。
  • 这个变量是调用va_ start 来初始化的。它的第一个参数是va_ list的变量名,第2个参数是省略号前最后一个有名字的参数。初始化过程把arg变量设置为指向可变参数部分的第一个参数。

  • 为了访问参数,需要使用va_ arg,这个宏接受两个参数 : va_.list变量和参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。va_ arg 返回这个参数的值,并使用va_ arg指向下一个可变参数。

  • 最后,当访问完毕最后一个可变参数之后,我们需要调用va_ end。

 

我们发现,由于可变量参数列表中的元素是未知的,所以在使用可变参数的时候有很多的限制。

注意:(可变参数列表最重要的是找到最开始已知的参数的地址)

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

 

 案例实现:

  • 利用可变量参数求解多个任意数的最大值

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 
#include 

int MAX(int n, ...)
{
	va_list arg;
	int max = 0;
	int i = 0;
	va_start(arg, n);
	max = va_arg(arg, int);//假设第一个值是最大的
	for (i = 1; i < n; i++)
	{
		int tmp = va_arg(arg, int);
		if (tmp>max)
		{
			max = tmp;
		}
	}
	return max;
}

int main()
{
	int max = MAX(4, 5, 6, 4, 2);
	printf("max = %d\n", max);
	system("pause");
	return 0;
}
  • 用print模拟实现printf函数

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 
#include 

void show(int num)
{
	if (num > 9)
	{
		show(num / 10);
	}
	putchar(num % 10 + '0');

}

void print(char *format, ...)
{
	va_list arg;
	va_start(arg, format);
	while (*format)
	{
		switch (*format)
		{
		case 's':
			//字符串
			 {
			char *ret = va_arg(arg, char*);
			while (*ret)//循环打印出来
			{
				putchar(*ret);
				ret++;
			}
			 }
			break;
		case 'd':
			//整型
			{
			int ret = va_arg(arg, int);
			show(ret);//转化为字符打印
			}
			break;
		case 'c':
			//字符
		    {
			putchar(va_arg(arg, char));
			break;
		     }
		default:
			putchar(*format);
			break;
		}
		format++;//遇到空格,此次类型结束,需要往后移动
	}
}


int main()
{
	print("s d c\n", "hello", 520, 'x','i','a');
	print("s ccc d\n", "hello", 'b', 'i', 't',100);
	system("pause");
	return 0;
}

 

你可能感兴趣的:(总结)