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