c语言中可变参数列表有关的内容在stdarg.h头文件中。这个头文件中的内容可以让函数实现类似scanf函数的功能,接收不确定个数的参数。
使用方法稍微复杂一些,必须按照如下步骤进行:
1.在函数原型中使用省略号。
2.在函数定义中创建一个va_list类型的变量。
3.用宏将该变量初始化为一个参数列表。
4.用宏访问这个参数列表。
5.用宏完成清理工作。
下面详细介绍这些步骤,函数原型中的参数列表中至少有一个后跟省略号的参数,这个参数成为parmN,如:
1 2 3 4 5 6 7 8
| void f1(int n, ...);
int f2(int n, const char *s, ...);
char f3(char c1, ..., char c2);
double f4();
|
接下来,在头文件stdarg.h中声明的va_list类型代表一种数据对象,该数据对象用于存放参数列表中省略号部分代表的参数。可变参数函数的定义应该像下边这样:
1 2 3
| doube sum(int lim, ...) { va_list ap;
|
然后,函数将使用stdargs.h中定义的宏va_start()把参数列表复制到va_list变量中。宏va_start()有两个参数:va_list类型的变量和变量parmN。如前所述,这里两个参数分别为va和lim,所以,va_start()函数的调用如下所示:
下一步是访问参数列表中的内容,这涉及宏va_arg()的使用,该宏接受两个参数:一个va_list类型的变量和一个类型名。
第一次调用va_arg()时,它返回参数列表的第一项,下次调用返回第二项,一次类推。参数类型制定返回值的类型。例如,如果参数列表中第一个参数为double类型,第二个为int类型,那么可使用下列语句:
1 2 3 4 5
| double tic; int toc; ... tic = va_arg(ap, double); toc = va_arg(ap, int);
|
这里需要注意的是,实际参数的类型必须与说明的类型相匹配,如果第一个参数为10.0,那么前面的tic部分的代码正常工作;但是如果参数为10,代码就可能无法正常工作。这里不会像赋值过程中那样进行double到int的自动转换。
最后,应该使用va_end()完成清理工作。该宏接受一个va_list变量作为参数:
此后,只有用va_start()重新对ap初始化后,才能使用变量ap。
因为va_arg()不提供后退回先前参数的方法,所以保存va_list变量的副本是有用的,C99为此专门添加了宏va_copy()。该宏的两个参数均为va_list类型变量,它将第二个参数复制到第一个参数中:
1 2 3 4 5 6 7 8 9
| va_list ap; va_list apcopy; double tic; int toc; ... va_start(ap, lim); va_copy(apcopy, ap); tic = va_arg(ap, double); toc = va_arg(ap, int);
|
这时,虽然已从ap中删除了前两项,但是还可以从apcopy中重新获取这两项。
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
#include <stdio.h> #include <stdarg.h>
double sum(int num, ...) { va_list ap;
int i = 0; double res = 0.0; va_start(ap, num); for (i = 0; i < num; ++i) { res += va_arg(ap, double); } va_end(ap); return res; }
int main(int argc, char *argv[]) { double s = 0.0;
s = sum(3, 1.1, 2.5, 13.3); printf("sum(3, 1.1, 2.5, 13.3) = %.2f\n", s); s = sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1); printf("sum(6, 1.1, 2.5, 13.1, 4.1, 5.1, 6.1) = %.2f\n", s);
return 0; }
|
上边也测试了,传进来的参数不对应的情况,发生了错误。
下面是另一个程序,说明了va_arg传入的类型还可以是自定义的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
#include <stdio.h> #include <stdarg.h>
struct point_t { int x, y; };
struct point_t add(int num, ...) { va_list ap; va_start(ap, num);
int i = 0; struct point_t p; struct point_t res; res.x = 0; res.y = 0; for (i = 0; i < num; ++i) { p = va_arg(ap, struct point_t); res.x += p.x; res.y += p.y; } va_end(ap); return res; }
int main(int argc, char *argv[]) { struct point_t p; struct point_t sum; p.x = 1; p.y = 2;
sum = add(3, p, p, p);
printf("add(3, p, p, p) = {%d, %d}\n", sum.x, sum.y);
return 0; }
|
下面说说stdio.h中与va_list相关的几个函数:
这几个函数有vprintf,vfprintf,vsprintf,vsnprintf。
函数原型分别为:
1 2 3 4
| int vprintf(const char *format, va_list ap); int vfprintf(FILE *stream, const char *format, va_list ap); int vsprintf(char *str, const char *format, va_list ap); int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
这几个函数分别与printf,fprintf,sprintf,snprintf类似,不过,他们接受一个va_list类型的变量,而不是参数列表。
利用这几个函数可以实现方便的函数如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
#include <stdio.h> #include <stdlib.h> #include <stdarg.h>
#define BUFSIZE 1024
int execute_cmd(const char *fmt, ...) { char cmd[BUFSIZE]; va_list ap; va_start(ap, fmt); vsprintf(cmd, fmt, ap); va_end(ap); return system(cmd); }
int main(int argc, char *argv[]) { execute_cmd("echo %s", "dir"); execute_cmd("ls %s", "/");
return 0; }
|
这里边的大部分内容都是从书上摘下来的,程序部分经过了修改。
参考:
1)C Primer Plus(第五版)中文版 16.13
2)hustoj项目源代码
Last updated: