link:http://blog.chinaunix.net/space. ... blog&id=2808215 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
- /*
- *功能:求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
- /*
- *功能:求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
- #include
- /*
- * 功能: 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
- #include
- #include
- #include
-
- /*
- *功能:和并多个字符串,然后返回。
- *输入:
- * 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版 |