C语言中处理函数可变参数的几个宏:stdarg
一、stdarg 简介:
在编写程序时,有时会遇到可变参数个数的函数,这种函数一般都使用了stdarg.h这个头文件中的几个宏。
下面给出 Linux manual 中的介绍。
NAME
stdarg - variable argument lists
名称
stdarg-variable argument lists // stdarg :可变参数列表。
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
概要
#include <stdarg.h> // 所在头文件void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
二、stdarg.h的描述
DESCRIPTION //描述
A function may be called with a varying number of arguments of varyingtypes. The include file <stdarg.h> declares a type va_list and defines
three macros for stepping through a list of arguments whose number and
types are not known to the called function.
一个参数类型和参数个数都不确定(变化)的函数可能被调用。在include <stdarg.h> 文件中,声明了一个va_list类型,并且定义了3个宏 (macro)用来遍历对一个参数列表,但这个参数列表中的参数的类型和参数的个数都是不确定的,在这个可变参数的函数被调用前。
The called function must declare an object of type va_list which is
used by the macros va_start(), va_arg(), and va_end().
这个被调用的可变参数函数 必须声明一个va_list类型的的对象,因为宏 va_start(), va_arg(),和va_end()使用这个对象。
1. 宏 va_start
void va_start(va_list ap, last);
The va_start() macro initializes ap for subsequent use by va_arg() and va_end(), and must be called first.宏va_start() 必须被首先调用; 其初始化ap对象,ap对象,将在后续的宏va_arg(), va_end()宏调用中使用。
The parameter last is the name of the last parameter before the variable argument list, i.e., the last parameter of which the calling func tion knows the type.
参数last 是变参数列表前的最后一个参数的名字(变量名),也就是说函数所能确定的参数中的最后一个。
Because the address of this parameter may be used in the va_start() macro, it should not be declared as a register variable, or as a function or an array type.
因为这个参数的地址在宏va_start()中可能被用到,所以它不能被声明为寄存器变量,或者函数、数组类型。
2. 宏 va_arg
type va_arg(va_list ap, type);
The va_arg() macro expands to an expression that has the type and value of the next argument in the call. The parameter ap is the va_list ap initialized by va_start(). Each call to va_arg() modifies ap so that the next call returns the next argument. The parameter type is a type name specified so that the type of a pointer to an object that has the specified type can be obtained simply by adding a * to type.
宏va_arg() 将函数未知参数列表中的下一个参数扩展为具有的值和类型的一个表达式。 参数ap 是被va_start()初始化的 va_list ap.
每一次调用va_arg 都修改ap的值,以致下次调用返回下一个参数的值。 参数type是一个指定的类型,因此指向这中类型的对象的指针能够简单获得通过*(dereference)获得这种类型。
The first use of the va_arg() macro after that of the va_start() macro returns the argument after last. Successive invocations return the values of the remaining arguments.
在使用了宏va_start(ap, last)之后,首次使用宏va_arg(ap, type) ,返回紧接着last的那个参数的值。后继的调用,返回剩余参数的值。
If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), random errors will occur.
如果,没有下一个参数,或者类型与下一个参数的类型不兼容(声明的类型与参数的默认类型),不确定的错误将发生。
If ap is passed to a function that uses va_arg(ap,type) then the value of ap is undefined after the return of that function.
如果 ap 传递给一个使用了va_arg(ap, type)函数 ,那么ap的值在函数返回后将是不确定的,
3. 宏 va_end
void va_end(va_list ap);
Each invocation of va_start() must be matched by a corresponding invocation of va_end() in the same function. After the call va_end(ap) the variable ap is undefined. Multiple transversals of the list, each bracketed by va_start() and va_end() are possible. va_end() may be a macro or a function.
在同一个函数中,每一次调用va_start() 都必须有一个va_end调用语气配对. 在调用va_end(ap) 后,ap的值将是不确定的。多次遍历参数列表时,每次都要以va_start(ap, last)开始,以va_end(ap)结束,才是可行的。
va_end(ap) 可能是个函数,也可能是个宏。
4. 宏 va_copy
void va_copy(va_list dest, va_list src);
An obvious implementation would have a va_list be a pointer to the stack frame of the variadic function. In such a setup (by far the most common) there seems nothing against an assignmentva_list aq = ap;
Unfortunately, there are also systems that make it an array of pointers (of length 1), and there one needs
va_list aq;
*aq = *ap;
Finally, on systems where parameters are passed in registers, it may be necessary for va_start() to allocate memory, store the parameters there, and also an indication of which parameter is next, so that va_arg() can step through the list. Now va_end() can free the allocated memory again. To accommodate this situation, C99 adds a macro va_copy(), so that the above assignment can be replaced by
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
Each invocation of va_copy() must be matched by a corresponding invoca tion of va_end() in the same function. Some systems that do not supply va_copy() have __va_copy instead, since that was the name used in the draft proposal.
EXAMPLE
The function foo takes a string of format characters and prints out the
argument associated with each format character based on the type.
#include <stdio.h>
#include <stdarg.h>
void foo(char *fmt, ...) {
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch(*fmt++) {
case ’s’: /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case ’d’: /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case ’c’: /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
CONFORMING TO
The va_start(), va_arg(), and va_end() macros conform to C89. C99 defines the va_copy() macro.
COMPATIBILITY
These macros are not compatible with the historic macros they replace. A backward compatible version can be found in the include file varargs.h.
COMPARISON
The historic setup is:
#include <varargs.h>
void foo(va_alist) va_dcl {
va_list ap;
va_start(ap);
while(...) {
...
x = va_arg(ap, type);
...
}
va_end(ap);
}
On some systems, va_end contains a closing ’}’ matching a ’{’ in va_start, so that both macros must occur in the same function, and in a way that allows this.
BUGS
Unlike the varargs macros, the stdarg macros do not permit programmers to code a function with no fixed arguments. This problem generates work mainly when converting varargs code to stdarg code, but it also creates difficulties for variadic functions that wish to pass all of their arguments on to a function that takes a va_list argument, such as vfprintf(3).
Linux manual 手册介绍:
[wzb@embedded test]$ man stdarg | cat
STDARG(3) Linux Programmer’s Manual STDARG(3)
NAME
stdarg - variable argument lists
SYNOPSIS
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
DESCRIPTION
A function may be called with a varying number of arguments of varying
types. The include file <stdarg.h> declares a type va_list and defines
three macros for stepping through a list of arguments whose number and
types are not known to the called function.
The called function must declare an object of type va_list which is
used by the macros va_start(), va_arg(), and va_end().
va_start
The va_start() macro initializes ap for subsequent use by va_arg() and
va_end(), and must be called first.
The parameter last is the name of the last parameter before the vari-
able argument list, i.e., the last parameter of which the calling func-
tion knows the type.
Because the address of this parameter may be used in the va_start()
macro, it should not be declared as a register variable, or as a func-
tion or an array type.
va_arg
The va_arg() macro expands to an expression that has the type and value
of the next argument in the call. The parameter ap is the va_list ap
initialized by va_start(). Each call to va_arg() modifies ap so that
the next call returns the next argument. The parameter type is a type
name specified so that the type of a pointer to an object that has the
specified type can be obtained simply by adding a * to type.
The first use of the va_arg() macro after that of the va_start() macro
returns the argument after last. Successive invocations return the
values of the remaining arguments.
If there is no next argument, or if type is not compatible with the
type of the actual next argument (as promoted according to the default
argument promotions), random errors will occur.
If ap is passed to a function that uses va_arg(ap,type) then the value
of ap is undefined after the return of that function.
va_end
Each invocation of va_start() must be matched by a corresponding invo-
cation of va_end() in the same function. After the call va_end(ap) the
variable ap is undefined. Multiple transversals of the list, each
bracketed by va_start() and va_end() are possible. va_end() may be a
macro or a function.
va_copy
An obvious implementation would have a va_list be a pointer to the
stack frame of the variadic function. In such a setup (by far the most
common) there seems nothing against an assignment
va_list aq = ap;
Unfortunately, there are also systems that make it an array of pointers
(of length 1), and there one needs
va_list aq;
*aq = *ap;
Finally, on systems where parameters are passed in registers, it may be
necessary for va_start() to allocate memory, store the parameters
there, and also an indication of which parameter is next, so that
va_arg() can step through the list. Now va_end() can free the allocated
memory again. To accommodate this situation, C99 adds a macro
va_copy(), so that the above assignment can be replaced by
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
Each invocation of va_copy() must be matched by a corresponding invoca-
tion of va_end() in the same function. Some systems that do not supply
va_copy() have __va_copy instead, since that was the name used in the
draft proposal.
EXAMPLE
The function foo takes a string of format characters and prints out the
argument associated with each format character based on the type.
#include <stdio.h>
#include <stdarg.h>
void foo(char *fmt, ...) {
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch(*fmt++) {
case ’s’: /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case ’d’: /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case ’c’: /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
CONFORMING TO
The va_start(), va_arg(), and va_end() macros conform to C89. C99
defines the va_copy() macro.
COMPATIBILITY
These macros are not compatible with the historic macros they replace.
A backward compatible version can be found in the include file
varargs.h.
COMPARISON
The historic setup is:
#include <varargs.h>
void foo(va_alist) va_dcl {
va_list ap;
va_start(ap);
while(...) {
...
x = va_arg(ap, type);
...
}
va_end(ap);
}
On some systems, va_end contains a closing ’}’ matching a ’{’ in
va_start, so that both macros must occur in the same function, and in a
way that allows this.
BUGS
Unlike the varargs macros, the stdarg macros do not permit programmers
to code a function with no fixed arguments. This problem generates
work mainly when converting varargs code to stdarg code, but it also
creates difficulties for variadic functions that wish to pass all of
their arguments on to a function that takes a va_list argument, such as
vfprintf(3).
2001-10-14 STDARG(3)