- 论坛徽章:
- 0
|
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 <stdio.h>
int printf(const char * fmt, ...);
"..."代表多个参数,可变。
当定义带有可变参数的函数时,需要的头文件是:<stdarg.h>. 其内定义了三个宏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 <stdarg.h>
void va_start(va_list ap, parmN);
无返回值
说明
在访问所有为命名的参数之前要调用宏va_start.它对ap进行初始化,也就是对可变参数进行定位。参数parmN是函数定义(在...之前的那个)可变参数表中最右边的固定参数的标识符。例如:
int printf(const char *fmt, ...);
fmt 就是parmN.所以一个函数必须至少声明一个固定的参数。
注意:
如果parmN用register存储类别, 一个函数或者数组类型,或者和应用默认的参数提升的类型不兼容的参数累声明,那么这种行为未定以。
va_arg宏
概述
#include <stdarg.h>
type va_arg(va_list ap, type)
返回值
第一次调用va_start后,对va_arg的第一次调用返回的是parmN制定的参数后面的参数值,后面的调用一次返回剩下的参数值。
说明
宏va_list展开为一个表达式,这个表达式的类型和值跟调用的下一个参数相同。va_arg 的每一次调用都会修改ap, 这样后面的参数值就会依次返回。
注意
如果实际不存在下一个参数,或者类型和实际存在的下一个参数类型不兼容,那么这种行为未定以。
va_end宏
概述
#include <stdarg.h>
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 <unistd.h>
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版 |
|