可变参数列表
头文件<stdarg.h>提供了遍历未知数目和类型的函数参数表的功能。该头文件的实现因不同的机器而不同,但提供的接口是一致的。
假定函数 f 带有可变数目的实际参数,lastarg 是它的最后一个命名的形式参数(参数列表必须至少包括一个命名参数)。那么,在函数 f 内声明一个类型为 va_list 的变量 ap (argument pointer),它将依次指向每个实际参数。
va_list ap;
在访问任何未命名的参数前,必须用 va_start 宏初始化 ap 一次。(va_start 将最后一个命名参数作为起点,将 ap 初始化为指向第一个未命名参数的指针)
此后,每次执行宏 va_arg 都将返回一个参数,并将 ap 指向下一个参数。 va_arg 使用一个类型名来决定返回的对象类型、指针移动的步长。
在所有的参数处理完毕之后,且在退出函数 f 之前,必须调用宏 va_end 以完成一些必要的清理工作。
Example
下面以实现函数printf的一个最简单版本为例,介绍如何以可移植的方式编写可处理变长参数列表的函数。因为我们的重点在于参数的处理,所以,函数minprintf只处理格式字符串和参数,格式转换则通过调用函数printf实现。
函数printf的正确声明形式为:
其中,省略号表示参数表中参数的数量和类型是可变的。省略号只能出现在参数表的尾部。
因为minprintf函数不需要像printf函数一样返回实际输出的字符数,因此,我们将它声明为下列形式:
void minprintf(char *fmt, ...)
编写函数minprintf的关键在于如何处理一个甚至连名字都没有的参数表。
#include <stdio.h>
#include <stdarg.h>
/* minprintf 函数:带有可变参数表的简化的printf函数 */
void minprintf(char *fmt, ...)
{
va_list ap; /* 依次指向每个无名参数 */
char *p, *sval;
int ival;
double dval;
va_start(ap, fmt); /* 将 ap 指向第一个无名参数 */
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch (*++p) {
case 'd':
ival = va_arg(ap, int);
printf("%d", ival);
break;
case 'f':
dval = va_arg(ap, double);
printf("%f", dval);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++)
#include <stdarg.h>
/* minprintf 函数:带有可变参数表的简化的printf函数 */
void minprintf(char *fmt, ...)
{
va_list ap; /* 依次指向每个无名参数 */
char *p, *sval;
int ival;
double dval;
va_start(ap, fmt); /* 将 ap 指向第一个无名参数 */
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch (*++p) {
case 'd':
ival = va_arg(ap, int);
printf("%d", ival);
break;
case 'f':
dval = va_arg(ap, double);
printf("%f", dval);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++)