Q如何编写一个可以接受可变数量参数的函数?
A使用<stdarg.h>头文件提供的设施。
这是一个将任意数量的字符串连接到malloc分配的内存中的函数
#include <stdlib.h> /* for malloc, NULL, size_t */
#include <stdarg.h> /* for va_ stuff */
#include <string.h> /* for strcat et al. */
char *vstrcat(const char *first, ...)
{
size_t len;
char *retbuf;
va_list argp;
char *p;
if(first == NULL)
return NULL;
len = strlen(first);
va_start(argp, first);
while((p = va_arg(argp, char *)) != NULL)
len += strlen(p);
va_end(argp);
retbuf = malloc(len + 1); /* +1 for trailing \0 */
if(retbuf == NULL)
return NULL; /* error */
(void)strcpy(retbuf, first);
va_start(argp, first); /* restart; 2nd scan */
while((p = va_arg(argp, char *)) != NULL)
(void)strcat(retbuf, p);
va_end(argp);
return retbuf;
}
(请注意,当参数列表第二次被处理时,需要再次调用va_start来重新扫描。注意对va_end的调用:它们对可移植性很重要,即使它们似乎什么也没做)。调用vstrcat的示例看起来类似于
char *str = vstrcat("Hello, ", "world!", (char *)NULL);
请注意最后一个参数的强制类型转换;请参见问题 5.2 和 15.3。(另请注意,调用者必须释放返回的、malloc被分配的存储空间)。vstrcat接受可变数量的参数,所有参数的类型都是char *。这里有一个接受不同类型可变数量参数的示例;它是熟悉的printf函数的一个精简版本。请注意,每次调用va_arg()都会指定正在从参数列表中检索的参数的类型。
(此处的miniprintf函数使用问题 20.10 中的baseconv来格式化数字。它非常不完美,因为它通常无法正确打印最小整数,INT_MIN。)
#include <stdio.h>
#include <stdarg.h>
#ifdef MAIN
void miniprintf(const char *, ...);
main()
{
miniprintf("Hello, world!\n");
miniprintf("%c %d %s\n", '1', 2, "three");
miniprintf("%o %d %x\n", 10, 10, 10);
miniprintf("%u\n", 0xffff);
return 0;
}
#endif
extern char *baseconv(unsigned int, int);
void
miniprintf(const char *fmt, ...)
{
const char *p;
int i;
unsigned u;
char *s;
va_list argp;
va_start(argp, fmt);
for(p = fmt; *p != '\0'; p++) {
if(*p != '%') {
putchar(*p);
continue;
}
switch(*++p) {
case 'c':
i = va_arg(argp, int);
/* *not* va_arg(argp, char); see Q 15.10 */
putchar(i);
break;
case 'd':
i = va_arg(argp, int);
if(i < 0) {
/* XXX won't handle INT_MIN */
i = -i;
putchar('-');
}
fputs(baseconv(i, 10), stdout);
break;
case 'o':
u = va_arg(argp, unsigned int);
fputs(baseconv(u, 8), stdout);
break;
case 's':
s = va_arg(argp, char *);
fputs(s, stdout);
break;
case 'u':
u = va_arg(argp, unsigned int);
fputs(baseconv(u, 10), stdout);
break;
case 'x':
u = va_arg(argp, unsigned int);
fputs(baseconv(u, 16), stdout);
break;
case '%':
putchar('%');
break;
}
}
va_end(argp);
}
另请参见问题 15.7。
参考文献:K&R2 第 7.3 节 第 155 页,第 B7 节 第 254 页
ISO 第 7.8 节
Rationale 第 4.8 节
H&S 第 11.4 节 第 296-9 页
CT&P 第 A.3 节 第 139-141 页
PCS 第 11 节 第 184-5 页,第 13 节 第 242 页