1. 引言
C语言我们接触的第一个库函数是
printf(“hello,world!”);其参数个数为1个。
然后,我们会接触到诸如:
printf(“a=%d,b=%s,c=%c”,a,b,c);此时其参数个数为4个。
在linux下,输入man 3 printf,可以看到prinf函数原型如下:
SYNOPSIS #include <stdio.h> int printf(const char *format, ...);
后面的三个点...表示printf参数个数是不定的.
如何实现可变参数函数?
2. 编写可变函数的素材准备
为了编写可变参数函数,我们需要用到<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是用于存放参数列表的数据结构。
va_start函数根据初始化last来初始化参数列表。
va_arg函数用于从参数列表中取出一个参数,参数类型由type指定。
va_copy函数用于复制参数列表。
va_end函数执行清理参数列表的工作。
函数官方说明,如果你看到英文就烦,可以自行忽略以下说明。
va_start() The va_start() macro initializes ap for subsequent use by va_arg() and va_end(), and must be called first. The argument last is the name of the last argument before the variable argument list, that is, the last argument of which the calling function knows the type. Because the address of this argument may be used in the va_start() macro, it should not be declared as a register variable, or as a func‐ tion or an array type. va_arg() The va_arg() macro expands to an expression that has the type and value of the next argument in the call. The argument ap is the va_list ap initialized by va_start(). Each call to va_arg() modifies ap so that the next call returns the next argument. The argument type is a type name specified so that the type of a pointer to an object that has the specified type can be obtained simply by adding a * to type. The first use of the va_arg() macro after that of the va_start() macro returns the argument after last. Successive invocations return the values of the remaining arguments. If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), random errors will occur. If ap is passed to a function that uses va_arg(ap,type) then the value of ap is undefined after the return of that function. va_end() Each invocation of va_start() must be matched by a corresponding invo‐ cation of va_end() in the same function. After the call va_end(ap) the variable ap is undefined. Multiple traversals of the list, each brack‐ eted by va_start() and va_end() are possible. va_end() may be a macro or a function.
GNU给出的一个实例:
#include <stdio.h> #include <stdarg.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\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
说明:
va_start(ap, fmt);用于根据fmt初始化可变参数列表。
va_arg(ap, char *);用于从参数列表中取出一个参数,其中的char *用于指定所取的参数的类型为字符串。每次调用va_arg后,参数列表ap都会被更改,以使得下次调用时能得到下一个参数。
va_end(ap);用于对参数列表进行一些清理工作。调用完va_end后,ap便不再有效。
以上程序给了我们一个实现printf函数的是思路,即:通过调用va_start函数,来得到参数列表,然后我们一个个取出参数来进行输出即可。
例如:对于printf(“a=%d,b=%s,c=%c”,a,b,c)语句;fmt的值为a=%d,b=%s,c=%c,调用va_start函数将参数a,b,c存入了ap中。注意到:fmt中的%为特殊字符,紧跟%后的参数指明了参数类型.
因此我们的简易printf函数如下:
#include <stdio.h> #include <stdarg.h> void myprintf(char *fmt, ...) { va_list ap; int d; double f; char c; char *s; char flag; va_start(ap,fmt); while (*fmt){ flag=*fmt++; if(flag!='%'){ putchar(flag); continue; } flag=*fmt++;//记得后移一位 switch (flag) { case 's': s=va_arg(ap,char*); printf("%s",s); break; case 'd': /* int */ d = va_arg(ap, int); printf("%d", d); break; case 'f': /* double*/ d = va_arg(ap,double); printf("%d", d); break; case 'c': /* char*/ c = (char)va_arg(ap,int); printf("%c", c); break; default: putchar(flag); break; } } va_end(ap); } int main(){ char str[10]="linuxcode"; int i=1024; double f=3.1415926; char c='V'; myprintf("string is:%s,int is:%d,double is:%f,char is :%c",str,i,f,c); }
int sum(int cnt,...){ int sum=0; int i; va_list ap; va_start(ap,cnt); for(i=0;i<cnt;++i) sum+=va_arg(ap,int); va_end(ap); return sum; }