prev up next   top/contents search

comp.lang.c FAQ 列表· 第 15.4 题

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.215.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 页


prev up next   contents search
关于此 FAQ 列表   关于 Eskimo   搜索   反馈   版权

Eskimo North 托管