1、应用与原理
在C语言中,有时我们无法给出一个函数参数的列表,比如:
int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...);
这时我们使用到了可以变参数,也就是使用...代表0个或多个参数。
那么编译器如何获取/使用这些参数。这涉及到参数的传递原理:
参数传递原理:
在内存中,函数的参数以栈的方式存取,从右到左入栈。这些参数存放的地址是连续的。这样,我们就可以通过获取第一个参数的地址,以及各个参数的地址偏移量,就可以获取每个参数的地址,从而得到每一个参数。
2、va_start、va_arg、va_end、va_copy介绍
#include <stdarg.h> void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); void va_copy(va_list dest, va_list src);
va_list是一个指向参数首地址的指针,
typedef struct { char *a0; /* pointer to first homed integer argument */ int offset; /* byte offset of next parameter */ } va_list;
va_start
对ap进行了一系列的初始化,之后ap会被va_arg和va_end使用到。last是可变参数...的前一个参数,如fun(char
*fmt, ...) 中的fmt。通过va_start初始化ap,我们就获得了可变参数前一个参数fmt的地址。
va_arg
va_arg用于获取可变参数...的每一个参数。如函数fun(char *fmt, ...)的一次调用fun(fmt, arg1, arg2, arg3)。在使用va_start()进行ap的初始化后,我们调用一次va_arg(ap,type)就获得了参数arg1,在调用一次就获得arg2,……从而,得到每一个参数的值。
va_end
va_end用于清理ap的值,与va_start()配对使用。
va_copy
不常用,暂不介绍。
3、Example
先给一个man手册里的例子
//foo.c #include <stdarg.h> #include <stdio.h> void foo(char *fmt, ...) { va_list ap; int d; char c, *s; va_start(ap, fmt); while (*fmt) switch (*fmt++) { case 's': /* string */ s = va_arg(ap, char *); printf("string %s ", s); break; case 'd': /* int */ d = va_arg(ap, int); printf("int %d ", d); break; case 'c': /* char */ /* need a cast here since va_arg only /* need a cast here since va_arg only takes fully promoted types */ c = (char) va_arg(ap, int); printf("char %c ", c); break; } va_end(ap); }
//main.c #include <stdio.h> #include <stdarg.h> #include "foo.h" int main(void) { foo("%s %d %c %d %c", "Hello", 4, 'x', 3, 'y'); return 0; }
执行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe string Hello int 4 char x int 3 char y windeal@ubuntu:~/Windeal/apue$
使用vsprintf的例子
//foo.c #include <stdarg.h> #include <stdio.h> void foo(char *fmt, ...) { va_list ap; int len = 0; char buf[64]; va_start(ap, fmt); len = vsnprintf(buf, 128, fmt, ap); va_end(ap); int i = 0; for(i = 0; i < len; i++) { putchar(buf[i]); } return ; }
// main.c #include <stdio.h> #include <stdarg.h> #include "foo.h" int main(void) { foo("Test:%s %d %c %d %c ", "Hello", 4, 'x', 3, 'y'); return 0; }
执行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe Test:Hello 4 x 3 y