首先了解一下什么是可变参数
C语言中的可变参数
1、C语言中的可变参数列表:C语言中有时候会遇到一些参数个数是可变的函数,像scanf,printf等,这些函数内部的参数列表都是可变的。通过将函数实现为可变参数的形式,使得函数可以接受1个以上的任意多个参数。(不固定)
如函数原型:scanf(const char * format,…);
解释:除了format以外,后面跟着的参数 个数,类型是可变的,用…作为一种占位符号。“…”称为可变参数列表,可以用来接受个数和类型不确定的参数。
再例如printf()是一个支持可变参数的函数,可以有多个参数。函数原型:int printf(const char *format,...) 省略号代表参数的个数是不固定的。
(1)可变参数列表在C语言中使用这样的三个宏(va_start,va_arg,va_end)和一个类型(va_list)来进行实现的,他们是定义在stdarg.h头文件中的。
1)va_start原型:
void va_start(va_list ap,paramN);
参数:va_list:存储参数的类型信息
ap:可变参数列表地址
paramN:确定的参数
2)va_arg原型:
void va_arg(va_list ap,type);返回下一个参数的值
3)va_end原型:
void va_end(va_list ap,type);关闭初始化的列表
使用方式:
1) 使用va_list初始化可变参数列表
2) 使用va_arg逐个获取参数值
3)用va_end将可变参数列表清空
先看一个例子:
实现一个函数可以求任意个参数的平均值
#include <stdio.h>
#include <stdarg.h>
int average(int n, ...)
{
va_list arg;//创建一个字符指针变量va_list相当于字符指针char*
int i = 0;
int sum = 0;
va_start(arg, n);//用n的地址初始化arg
for (i = 0; i<n; i++)
{ //获取参数va_arg(va_list arg,type),一定要保证类型正确
sum += va_arg(arg, int);
}
return sum / n;
va_end(arg);//arg=NULL将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;
}
看一下运行结果:
avg1=2
avg2=2
说明: 声明一个 va_list 类型的变量 arg ,它用于访问参数列表的未确定部分。这个变量是调用 va_start 来初始化的。它的第一个参数是 va_list 的变量名,第2个参数是省略号前最后一个有名字的参数。初始化过程把 arg 变量设置为指向可变参数部分的第一个参数。为了访问参数,需要使用 va_arg ,这个宏接受两个参数: va_list 变量和参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。va_arg````返回这个参数的值,并使用 va_arg```指向下一个可变参数。最后,当访问完毕最后一个可变参数之后,我们需要调用 va_end 。 头文件<stdarg.h>提供了
遍历未知数目和类型的函数参数列表的功能。
2、实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:
①函数栈的生长方向
②参数的入栈顺序
③CPU的对齐方式
④内存地址的表达方式
3、可变参数的限制 :
- 可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
- 参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。
- 这些宏是无法直接判断实际存在参数的数量。
- 这些宏无法判断每个参数的是类型。
- 如果在va_arg中指定了错误的类型,那么其后果是不可预测的。