TABLE
1.介绍
2.使用
2.1 代码实例,简单体会。
2.2三个主要的宏
2.3可变参数的个数确定
2.4重扫描
2.5 更多的代码实例
3.注意事项/限制
4.参考资源
1.介绍
c语言有一个强大的功能,就是它允许定义可接受可变参数列表的函数。如:
#include
int printf(const char * fmt, ...);
"..."代表多个参数,可变。
当定义带有可变参数的函数时,需要的头文件是:. 其内定义了三个宏va_start,va_arg,va_end,和一个数据类型va_list来实现我们的需求。
2.使用
2.1 代码实例,简单体会。
- filename: sum_n_nums.c
-
#include <stdio.h>
-
/*
-
*功能:求n个整数的加和,并返回
-
*输入:
-
* int n,欲相加的数的个数
-
* ... 可变参数
-
* 返回:
-
* double, 加和的结果
-
*/
-
-
double sum_n_nums(int n, ...)
-
{
-
double isum;
-
int i;
-
va_list ap;//va_list 类型
-
-
va_start(ap, n); //初始化ap,定位
-
for (i = 0, isum = 0; i < n; i++) {
-
isum += va_arg(ap, int); //一次取得可变参数
-
}
-
va_end(ap);//清理
-
-
return isum;
-
}
-
-
int
-
main()//测试用例
-
{
-
printf("%.2f\n" ,sum_n_nums(10 ,123 ,123 ,5434 ,123 ,
-
123 ,5434 ,123 ,123 ,5434 ,23));
-
return 0;
-
}#include <stdio.h>
-
/*
-
*功能:求n个整数的加和,并返回
-
*输入:
-
* int n,欲相加的数的个数
-
* ... 可变参数
-
* 返回:
-
* double, 加和的结果
-
*/
-
-
double sum_n_nums(int n, ...)
-
{
-
double isum;
-
int i;
-
va_list ap;//va_list 类型
-
-
va_start(ap, n); //初始化ap,定位
-
for (i = 0, isum = 0; i < n; i++) {
-
isum += va_arg(ap, int); //一次取得可变参数
-
}
-
va_end(ap);//清理
-
-
return isum;
-
}
-
-
int
-
main()//测试用例
-
{
-
printf("%.2f\n" ,sum_n_nums(10 ,123 ,123 ,5434 ,123 ,
-
123 ,5434 ,123 ,123 ,5434 ,23));
-
return 0;
-
}
2.2三个主要的宏
va_list
它是一个适合保存va_start, va_arg, va_end所需信息的类型。如要访问不同的参数,那么调用函数要声明一个va_list类型的数据对象。(常用ap作为其名字)。
va_start宏
概述
#include
void va_start(va_list ap, parmN);
无返回值
说明
在访问所有为命名的参数之前要调用宏va_start.它对ap进行初始化,也就是对可变参数进行定位。参数parmN是函数定义(在...之前的那个)可变参数表中最右边的固定参数的标识符。例如:
int printf(const char *fmt, ...);
fmt 就是parmN.所以一个函数必须至少声明一个固定的参数。
注意:
如果parmN用register存储类别, 一个函数或者数组类型,或者和应用默认的参数提升的类型不兼容的参数累声明,那么这种行为未定以。
va_arg宏
概述
#include
type va_arg(va_list ap, type)
返回值
第一次调用va_start后,对va_arg的第一次调用返回的是parmN制定的参数后面的参数值,后面的调用一次返回剩下的参数值。
说明
宏va_list展开为一个表达式,这个表达式的类型和值跟调用的下一个参数相同。va_arg 的每一次调用都会修改ap, 这样后面的参数值就会依次返回。
注意
如果实际不存在下一个参数,或者类型和实际存在的下一个参数类型不兼容,那么这种行为未定以。
va_end宏
概述
#include
void va_end(va_list ap);
无返回值
说明
促进函数的正常返回,进行整理工作。可能会对ap进行修改, 这样ap就不在有用了。
注意
如果没用执行相应的va_start,或者没又在返回之前调用va_end, 那么这种行为未定以。
2.3可变参数的个数确定
因为参数个数是可变的,所以函数执行的时候确定传进来的参数个数是很重要的。va_arg宏处理不存在的参数行为为定义,我们不能用while( va_arg(ap, char *) != NULL) 类似的形式来确定结束条件。一下提出两类方法:
1.用最后一个固定参数对可变参数的个数进行确定。
例1
int printf(const char *fmt, ...);
给定了fmt,那么后面可变参数的个数就确定了。如fmt = “a is %d, b is %f, and c is %s \n", 通过控制格式,我们就可以确定需要传递,三个可变参数。
例2
int sum_n_nums(int n, ...);//传n个数,然后返回n个数的和。
可变参数的个数通过n来确定。
2.将可变参数的最后一个参数作为结束条件
例
#include
int execl(const char *pathname, const char *arg0, ...);
在使用这个函数的时候要求传递可变参数的最后一个参数需要是 NULL,也就是(char *) 0.这样我们就可以用 while (va_arg(ap, char *) != NULL)的形式来确定结束条件。
推荐的方式是,在每一次循环扫描内都执行一次va_start,va_end.推荐的方式是,在每一次循环扫描内都执行一次va_start,va_end.
2.4重扫描
推荐的方式是,在每一次循环扫描内都执行一次va_start,va_end.
2.5 更多的代码实例
- filename: minprintf.c
-
#include <stdio.h>
-
#include <stdarg.h>
-
/*
-
* 功能: printf函数的简体版
-
*/
-
-
-
void minprintf(char *fmt, ...)
-
{
-
va_list ap;
-
char *p, *sval;
-
int ival;
-
double dval;
-
-
va_start(ap, fmt);
-
for (p = fmt; *p; p++) { //通过fmt来控制可变参数(第一种方法)。
-
if (*p != '%') {
-
putchar(*p);
-
continue;
-
}
-
switch (*++p) {
-
case 'd':
-
ival = va_arg(ap, int);
-
printf("%d", ival);
-
break;
-
case 'f':
-
dval = va_arg(ap, double);
-
printf("%f", ival);
-
break;
-
case 's':
-
for (sval = va_arg(ap, char *); *sval; sval++)
-
putchar(*sval);
-
break;
-
default:
-
putchar(*p);
-
break;
-
}
-
}
-
va_end(ap);
-
}
-
//无测试用例
- filename: cat_strs.c
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <stdarg.h>
-
-
/*
-
*功能:和并多个字符串,然后返回。
-
*输入:
-
* char *first: 合并的第一个字符串
-
* ... 后继的多个字符串,要以(char *) 0结束
-
*返回:
-
* char * 合并后字符串指针
-
*注意:
-
* 合并后的字符串保存在静态空间;
-
* 如果要合并的字符串总长度 大于 静态太分配的 CAT_BUF_LEN 那么函数出错,返回。
-
* 传递的可变参数一定以NULL结束
-
*/
-
-
char *cat_strs(char *first, ...)
-
{
-
#ifndef CAT_BUF_LEN
-
#define CAT_BUF_LEN 1024
-
#endif
-
static char result[CAT_BUF_LEN];
-
va_list ap; //va_list 类型
-
char *pr;
-
int len_sum;
-
char *ptmp;
-
-
memset(result, '\0', sizeof (result));//初始化静态空间
-
-
va_start(ap, first);//初始化ap
-
// 判断合并的字符串总长度是否大于CAT_BUF_LEN
-
while ((ptmp = va_arg(ap, char *)) != NULL) //第二种方法控制结束条件
-
len_sum += strlen(ptmp);
-
len_sum += strlen(first);
-
if (len_sum > CAT_BUF_LEN -1) {
-
fprintf(stderr, "cat_strs: the CAT_BUF_LEN is not enought!\n");
-
va_end(ap);
-
exit(EXIT_FAILURE);
-
}
-
va_end(ap);
-
-
va_start(ap, first); //重扫描!
-
pr = result;
-
//合并字符串
-
strcat(pr, first);
-
while (ptmp = va_arg(ap, char *)) {
-
strcat(pr, ptmp);
-
}
-
va_end(ap);
-
-
return result;
-
}
-
-
int
-
main(void) //测试用例
-
{
-
char *result;
-
-
result = cat_strs("start ", "sajfklefjwle" ,"wejflsjfdghwejl",
-
"wejflsjfdghwejl", " end\n", NULL);//一定要以空指针结束
-
fputs(result, stdout);
-
exit(0);
-
}
3.注意事项/限制
1. 声明函数时必须至少有一个固定的参数,最后一个固定参数引用时候习惯上称为parmN。
2. 必须在函数内执行va_start(ap, parmN),之后才能执行va_arg,或va_end。
3 .对于va_arg,不能编写类型T,使得当它作为一个参数传递时范围会变大。例如,不能用double代替float,int代替char等。
可以编写的类型T,仅仅通过在其后面加一个*就可以转换为相应的指针类型。
4 .前面执行了va_start,那么必须后面执行va_end.一旦执行了va_end,那么不能再执行va_arg。除非再次执行va_start, 进行重扫描。
4.参考资源
《c标准库》,P.J.Plauger 著,卢红星 徐明亮 霍建同 译, 2009年第1版
《c程序设计语言 第2版》,Brian W.Kernighan/ Dennis M.Ritchie 著, 徐宝文 李志 译, 2009年第2版
《unix 环境高级编程 第2版》,W。Richard Stevens Stephen A。Rago 著, 尤晋元 张亚英 戚正伟 译, 2006年第1版
阅读(2277) | 评论(0) | 转发(0) |