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 页