可变参数列表,stdarg
2017-10-17 01:24
357 查看
所谓“可变参数列表”就是指函数的形参的数目和类型是不确定的。printf函数就是一个可变参数的函数,第一个参数是格式化字符串,后面可以跟任意数目的参数。而我们平时使用的函数,其参数的数目和类型都是固定的,一旦声明,无法改变。
1.计算一系列值的平均值
(注意:这些值不是保存在数组中的,而是在参数中显示的传递)
float average( int n_values, int v1, int v2, int v3, int v4, int v5 )
{
float sum =v1;
if( n_values>=2 )
{
sum += v2;
}
else if( n_values>=3 )
{
sum += v3;
}
else if( n_values>=4 )
{
sum += v4;
}
else if( n_values>=5 )
{
sum += v5;
}
return sum/n_values;
}
但是这种实现方式的缺点很明显:只能计算5个数的平均数,调用时加上n_values一共能且只能传递6个参数,如果计算平均数的个数小于5,还要补0。
2. 在C中实现可变参数列表
C语言中使用头文件stdarg.h来实现可变参数。stdarg.h属于标准库,其声明了一个类型va_list和三个宏(va_start、va_arg、va_end)。
具体的使用步骤如下:
1. 在函数声明中使用 省略号;
2. 在函数体中穿件一个va_list类型的变量;
3. 用宏va_start将该变量初始化为一个参数列表;
4. 用宏va_arg访问这个参数列表;
5. 用宏va_end完成清理工作;
下面分别介绍它们的使用方法:
[u]2.1 在声明中使用省略号(就是3个句点)。 [/u]
也就是说函数的声明要写成类似下面的形式:void(int n_values, …)。和普通函数一样,书写该函数的原型时,要和函数的定义保持一致,即也要使用省略号。
2.2 声明一个va_list类型的变量。
声明方式为:va_list arg; 可以讲这里的变量名称arg替换为其它名称。
2.3 用宏va_start将该变量初始化为一个参数列表。
作用是将调用时传递的参数列表复制到va_list变量中。
va_start需要两个参数:va_list类型变量的名称,省略号前最后一个命名参数。
2.4 用宏va_arg访问这个参数列表。
只能从前往后依次访问,即第一次调用返回参数列表的第一项,第二次调用返回第二项。
va_arg需要两个参数:va_list类型变量的名称,下一个变量的类型。
2.5 用宏ar_end完成清理工作。
例如:释放动态分配的用于存放可变参数的内存等。
va_end需要一个参数:va_list类型变量的名称。在调用va_end以后,只有通过va_start重新对列表进行初始化以后,才能继续使用变量arg。
3.用 可变参数列表 解决上面的均值问题
代码如下:
//n_values的第一个作用用于提示...的位置
//n_values的第二个作用用于记录...数据的个数
float average2(int n_values, ... )
{
va_list arg; //定义指针,char *arg;
int count;
float sum = 0;
va_start( arg, n_values ); //将指针定位到...的开头
int temp;
for( count=0; count
{
temp = va_arg( arg, int );
sum += temp; //从...中取数据
}
va_end( arg ); //关闭指针list = NULL;
return sum/n_values;
}
4.注意事项:
(1). 在省略号之前必须有一个命名参数。
因为如果连一个命名参数都没有,那么无法使用va_start。其实在C的内部实现中,是通过在省略号之前、并且紧挨着省略号的该变量来确定 可变参数的开始位置,所以必须要有至少一个命名参数。
(2). 在省略号之后不能再有命名参数。
因为可变参数的长度可以变化,如果在省略号之后还有命名参数,那么编译器无法确定应该把这个命名参数当做可变参数,还是应该当做命名参数,会引起歧义。
(3). 无法直接获得可变参数列表中的参数的数量和类型。
如果需要获得参数的数量和类型,就必须使用其他标志。
例如:在printf函数中,通过“格式化字符串”中的“格式化字符”(如%d)既指定了可变参数的数量,也指定了可变参数的类型。
(4). 不能在va_arg中指定错误的类型。
例如:上面的程序中,可变参数部分如果含有一个浮点数2.0,那么其结果是未定义的。
原因分析:值在计算机中都是以0和1来存储的,一个值的类型无法简单的通过它被存储的位模式来判断,而要看它被解释成为什么类型。例如:位模式0100 0000 0100 1000 1111 0101 1100 0011被解释成单精度浮点数float值为3.14,被解释成整数值为1 078 523 331。所以如果指定了错误的类型,那么结果可能会相差千里。
(5). 注意参数传递时的“缺省类型提升”。
char、short、float类型的值将缺省提升为 int 和 double型。所以如果需要char、short和float类型时,在va_arg中需要使用int和double。原因与上一条注意事项(“不能在va_arg中指定错误的类型”)相同。
(6). 通过va_arg依次访问参数列表时,不能进行回退访问。
如果需要进行回退访问,可以使用宏va_copy,将va_list变量arg1拷贝成arg2,通过访问arg2来实现访问已经在arg1中已经访问过的参数。
5. 练习:
题目:编写一个max_list的函数,检查任意数目的整形参数并返回它们中的最大值。参数列表以一个负值结尾,用来表示列表的结束。
实现代码如下:
#include
#include
#include //可变长度参数需要包含的头文件
int max_list(int first,...)
{
va_list list; int max; //用来保存结果
if(first<0) //first为复数时,说明列表为空,返回0
{
return 0;
}
va_start(list,first); //用va_start初始化list,第一个参数为va_list的名字,第二个为省略号前的最后一个变量
max=first;
int tmp=va_arg(list,int);
//寻找最大数
while(tmp >=0)
{
if(tmp>max)
{
max=tmp;
}
tmp=va_arg(list,int); }
va_end(list); //结束时调用va_end()
return max;
}
int main()
{
printf("%d\n",max_list(2,4,7,9,1,19,-1));
printf("%d\n",max_list(-1));
printf("%d\n",max_list(0,-1));
return 0;
}
1.计算一系列值的平均值
(注意:这些值不是保存在数组中的,而是在参数中显示的传递)
float average( int n_values, int v1, int v2, int v3, int v4, int v5 )
{
float sum =v1;
if( n_values>=2 )
{
sum += v2;
}
else if( n_values>=3 )
{
sum += v3;
}
else if( n_values>=4 )
{
sum += v4;
}
else if( n_values>=5 )
{
sum += v5;
}
return sum/n_values;
}
但是这种实现方式的缺点很明显:只能计算5个数的平均数,调用时加上n_values一共能且只能传递6个参数,如果计算平均数的个数小于5,还要补0。
2. 在C中实现可变参数列表
C语言中使用头文件stdarg.h来实现可变参数。stdarg.h属于标准库,其声明了一个类型va_list和三个宏(va_start、va_arg、va_end)。
具体的使用步骤如下:
1. 在函数声明中使用 省略号;
2. 在函数体中穿件一个va_list类型的变量;
3. 用宏va_start将该变量初始化为一个参数列表;
4. 用宏va_arg访问这个参数列表;
5. 用宏va_end完成清理工作;
下面分别介绍它们的使用方法:
[u]2.1 在声明中使用省略号(就是3个句点)。 [/u]
也就是说函数的声明要写成类似下面的形式:void(int n_values, …)。和普通函数一样,书写该函数的原型时,要和函数的定义保持一致,即也要使用省略号。
2.2 声明一个va_list类型的变量。
声明方式为:va_list arg; 可以讲这里的变量名称arg替换为其它名称。
2.3 用宏va_start将该变量初始化为一个参数列表。
作用是将调用时传递的参数列表复制到va_list变量中。
va_start需要两个参数:va_list类型变量的名称,省略号前最后一个命名参数。
2.4 用宏va_arg访问这个参数列表。
只能从前往后依次访问,即第一次调用返回参数列表的第一项,第二次调用返回第二项。
va_arg需要两个参数:va_list类型变量的名称,下一个变量的类型。
2.5 用宏ar_end完成清理工作。
例如:释放动态分配的用于存放可变参数的内存等。
va_end需要一个参数:va_list类型变量的名称。在调用va_end以后,只有通过va_start重新对列表进行初始化以后,才能继续使用变量arg。
3.用 可变参数列表 解决上面的均值问题
代码如下:
//n_values的第一个作用用于提示...的位置
//n_values的第二个作用用于记录...数据的个数
float average2(int n_values, ... )
{
va_list arg; //定义指针,char *arg;
int count;
float sum = 0;
va_start( arg, n_values ); //将指针定位到...的开头
int temp;
for( count=0; count
{
temp = va_arg( arg, int );
sum += temp; //从...中取数据
}
va_end( arg ); //关闭指针list = NULL;
return sum/n_values;
}
4.注意事项:
(1). 在省略号之前必须有一个命名参数。
因为如果连一个命名参数都没有,那么无法使用va_start。其实在C的内部实现中,是通过在省略号之前、并且紧挨着省略号的该变量来确定 可变参数的开始位置,所以必须要有至少一个命名参数。
(2). 在省略号之后不能再有命名参数。
因为可变参数的长度可以变化,如果在省略号之后还有命名参数,那么编译器无法确定应该把这个命名参数当做可变参数,还是应该当做命名参数,会引起歧义。
(3). 无法直接获得可变参数列表中的参数的数量和类型。
如果需要获得参数的数量和类型,就必须使用其他标志。
例如:在printf函数中,通过“格式化字符串”中的“格式化字符”(如%d)既指定了可变参数的数量,也指定了可变参数的类型。
(4). 不能在va_arg中指定错误的类型。
例如:上面的程序中,可变参数部分如果含有一个浮点数2.0,那么其结果是未定义的。
原因分析:值在计算机中都是以0和1来存储的,一个值的类型无法简单的通过它被存储的位模式来判断,而要看它被解释成为什么类型。例如:位模式0100 0000 0100 1000 1111 0101 1100 0011被解释成单精度浮点数float值为3.14,被解释成整数值为1 078 523 331。所以如果指定了错误的类型,那么结果可能会相差千里。
(5). 注意参数传递时的“缺省类型提升”。
char、short、float类型的值将缺省提升为 int 和 double型。所以如果需要char、short和float类型时,在va_arg中需要使用int和double。原因与上一条注意事项(“不能在va_arg中指定错误的类型”)相同。
(6). 通过va_arg依次访问参数列表时,不能进行回退访问。
如果需要进行回退访问,可以使用宏va_copy,将va_list变量arg1拷贝成arg2,通过访问arg2来实现访问已经在arg1中已经访问过的参数。
5. 练习:
题目:编写一个max_list的函数,检查任意数目的整形参数并返回它们中的最大值。参数列表以一个负值结尾,用来表示列表的结束。
实现代码如下:
#include
#include
#include //可变长度参数需要包含的头文件
int max_list(int first,...)
{
va_list list; int max; //用来保存结果
if(first<0) //first为复数时,说明列表为空,返回0
{
return 0;
}
va_start(list,first); //用va_start初始化list,第一个参数为va_list的名字,第二个为省略号前的最后一个变量
max=first;
int tmp=va_arg(list,int);
//寻找最大数
while(tmp >=0)
{
if(tmp>max)
{
max=tmp;
}
tmp=va_arg(list,int); }
va_end(list); //结束时调用va_end()
return max;
}
int main()
{
printf("%d\n",max_list(2,4,7,9,1,19,-1));
printf("%d\n",max_list(-1));
printf("%d\n",max_list(0,-1));
return 0;
}
相关文章推荐
- C使用 stdarg 宏来实现函数的可变参数列表
- 可变参数列表通过宏来实现(头文件stdarg.h)
- 可变参数列表:stdarg宏
- stdarg(3) variable argument lists 可变参数列表
- 可变参数列表——stdarg宏
- 可变参数列表的宏和<stdarg.h>实现函数的可变参数列表
- [C语言]利用stdarg.h来实现可变参数列表
- 又学了一招——JAVA中三个点(...)的运算符,表示可变参数列表
- 可变参数列表剖析
- 修改java的可变参数列表的方法
- objective-c基础之可变参数列表va_list
- Java 可变参数列表
- C语言之利用可变参数列表实现简易的printf
- 可变参数列表
- JAVA类型后面跟3个点(可变长度参数列表)
- PHP可变长度参数列表的实用技巧
- 可变参数列表
- 用可变参数列表模拟实现printf函数
- c语言函数可变参数列表
- fprintf 的封装(vsprintf,va_start(), va_arg(), va_end()可变参数列表)