C语言里的可变参数
2009-11-26 14:29
162 查看
原创文章,转载请注明出处,谢谢!
作者:清林,博客名:
飞
空静渡
在
c
语言中有一种函数,它可以有可变参数,即是说它的参数的个数是不确定的。一个最典型的函数就是
printf
函数。其实我们自己也可以定义我们的可变参数的函数。
在
udev
的源代码里,我们也可以看到有可变参数的应用,在
libudev.c
源文件中有以下代码。
这是一个写
log
的函数,其中就用到了可变参数。
下面我们就来说一下可变参数的用法。
先看下使用可变参数都有哪几个宏。
在
c89
中定义的有
va_start(),
va_arg(),
和
va_end()
这三个宏,在
c99
中又增加了一
va_copy()
的宏。
在上面的代码中,我们已经看到了这几个洪的使用,下面我将用一个官方的例子来说明这几个宏的使用。下面是一个官方的例子代码:
下面我将一个一个的说明。
首先,看一下这几个宏的用法
:
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);
我们在使用任何的可变参数时,都必须先声明一个
va_list
变量,而且是在
va_start
之前声明,如上面的
va_list
ap
。在声明完这个变量后就可以使用这几个宏了。
void
va_start(va_list ap, last);
va_start
用来初始化我们刚才声明的
ap
变量,
last
就是我们的可变参数的前一个参数,如上面的代码中的
va_start(ap,
fmt)
,
fmt
就是我们可变参数
...
前的参数。在调用
va_start
初始化
ap
后,我们才可以使用
va_arg
和
va_end
这两个宏。
在使用
va_start
后
ap
会指向我们参数栈中的可变参数中的第一个参数(我们的函数的参数是放在栈里的,后面用图详细说明)。
type
va_arg(va_list ap, type);
va_arg
的作用是取可变参数里的一个参数。
ap
是我们
va_start
中初始化中的
ap
,
tyep
是由我们自己指定的一个类型,在调用
va_arg
后,会返回可变参数中的一个参数,并会使
ap
指向下一个参数。我们来看下上面的代码:
由上面的程序我们可以知道,如果
fmt
的值是
s
的话,那么它后面的第一个参数是一个字符串指针,我们将会取得这个字符串并赋给
s
变量并打印出来;如果
fmt
的值是
d
的话,那么它后面的第一个参数是一个整型值,我们就把这个值赋给变量
d
并打印出来等等。
我们可以看到,
va_arg
的第二个参数的类型就返回值的类型,我们为什么要用这个参数呢?我们看下图
,
下图
foo
函数的栈结构。
假设我们的可变参数是
int
n
,
char
c
;即我们的函数
void
foo(char
*fmt, …)
是这样的
void
foo(char
*fmt, int n, char c)
那么当我们使用
va_start(ap
,
fmt)
时,我们的
ap
就指向
int
n,
当我们调用
int
n1=va_arg(ap,
int)
时,我们的
n1
就等于
n
,而我们需要
int
这个参数是因为我们要知道
n
的大小,这样我们的
ap
就可以指向下一个产生
c
了,即在调用
va_arg(ap,
int)
后,
ap
指向
c
了,这样下次我们就可以方便的取
c
的值。如果
int
n
的类型出错,例如我们这样
short
s
=
va_arg(ap,
short)
或者根本没有
int
n
这个参数,即后面已经没有参数了,而我们还调用
va_arg
来获取参数,那么我们就会得到一个不确定的错误的。
void
va_end(va_list ap);
任何调用了
va_start
后,都必须要调用
va_end
来清除
ap
(
ap
的内部实现为一个指针)。
需要注意的一点是
,va_start
的宏的实现里带有
{
括号,而
va_end
的实现里带有
}
括号,所以这两个宏必须成对出现,而且只能出现在同一个函数里。
void
va_copy(va_list dest, va_list src);
一般我们会这样赋值:
va_list
aq = ap;
或者
va_list
aq;
*aq
= *ap;
我们看到我们前面的函数的参数是放在栈里的,但是有些系统的函数的参数是放在寄存器里的,因此
c99
就定义了这个
va_copy
。
va_list
aq;
va_copy(aq,
ap);
...
va_end(aq);
下面是详细说明的英文,大家参考看看(你可以在
linux
系统了的
man
3 stdarg
手册里看到)。
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 noth‐
ing
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 arguments are passed in registers, it may be
necessary for va_start() to allocate memory, store the arguments
there, and also an indication
of
which argument 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 invocation
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.
作者:清林,博客名:
飞
空静渡
在
c
语言中有一种函数,它可以有可变参数,即是说它的参数的个数是不确定的。一个最典型的函数就是
printf
函数。其实我们自己也可以定义我们的可变参数的函数。
在
udev
的源代码里,我们也可以看到有可变参数的应用,在
libudev.c
源文件中有以下代码。
void udev_log(struct udev *udev, int priority, const char *file, int line, const char *fn, const char *format, ...) { va_list args; va_start(args, format); udev->log_fn(udev, priority, file, line, fn, format, args); va_end(args); }
这是一个写
log
的函数,其中就用到了可变参数。
下面我们就来说一下可变参数的用法。
先看下使用可变参数都有哪几个宏。
在
c89
中定义的有
va_start(),
va_arg(),
和
va_end()
这三个宏,在
c99
中又增加了一
va_copy()
的宏。
在上面的代码中,我们已经看到了这几个洪的使用,下面我将用一个官方的例子来说明这几个宏的使用。下面是一个官方的例子代码:
#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); }
下面我将一个一个的说明。
首先,看一下这几个宏的用法
:
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);
我们在使用任何的可变参数时,都必须先声明一个
va_list
变量,而且是在
va_start
之前声明,如上面的
va_list
ap
。在声明完这个变量后就可以使用这几个宏了。
void
va_start(va_list ap, last);
va_start
用来初始化我们刚才声明的
ap
变量,
last
就是我们的可变参数的前一个参数,如上面的代码中的
va_start(ap,
fmt)
,
fmt
就是我们可变参数
...
前的参数。在调用
va_start
初始化
ap
后,我们才可以使用
va_arg
和
va_end
这两个宏。
在使用
va_start
后
ap
会指向我们参数栈中的可变参数中的第一个参数(我们的函数的参数是放在栈里的,后面用图详细说明)。
type
va_arg(va_list ap, type);
va_arg
的作用是取可变参数里的一个参数。
ap
是我们
va_start
中初始化中的
ap
,
tyep
是由我们自己指定的一个类型,在调用
va_arg
后,会返回可变参数中的一个参数,并会使
ap
指向下一个参数。我们来看下上面的代码:
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; }
由上面的程序我们可以知道,如果
fmt
的值是
s
的话,那么它后面的第一个参数是一个字符串指针,我们将会取得这个字符串并赋给
s
变量并打印出来;如果
fmt
的值是
d
的话,那么它后面的第一个参数是一个整型值,我们就把这个值赋给变量
d
并打印出来等等。
我们可以看到,
va_arg
的第二个参数的类型就返回值的类型,我们为什么要用这个参数呢?我们看下图
,
下图
foo
函数的栈结构。
假设我们的可变参数是
int
n
,
char
c
;即我们的函数
void
foo(char
*fmt, …)
是这样的
void
foo(char
*fmt, int n, char c)
那么当我们使用
va_start(ap
,
fmt)
时,我们的
ap
就指向
int
n,
当我们调用
int
n1=va_arg(ap,
int)
时,我们的
n1
就等于
n
,而我们需要
int
这个参数是因为我们要知道
n
的大小,这样我们的
ap
就可以指向下一个产生
c
了,即在调用
va_arg(ap,
int)
后,
ap
指向
c
了,这样下次我们就可以方便的取
c
的值。如果
int
n
的类型出错,例如我们这样
short
s
=
va_arg(ap,
short)
或者根本没有
int
n
这个参数,即后面已经没有参数了,而我们还调用
va_arg
来获取参数,那么我们就会得到一个不确定的错误的。
void
va_end(va_list ap);
任何调用了
va_start
后,都必须要调用
va_end
来清除
ap
(
ap
的内部实现为一个指针)。
需要注意的一点是
,va_start
的宏的实现里带有
{
括号,而
va_end
的实现里带有
}
括号,所以这两个宏必须成对出现,而且只能出现在同一个函数里。
void
va_copy(va_list dest, va_list src);
一般我们会这样赋值:
va_list
aq = ap;
或者
va_list
aq;
*aq
= *ap;
我们看到我们前面的函数的参数是放在栈里的,但是有些系统的函数的参数是放在寄存器里的,因此
c99
就定义了这个
va_copy
。
va_list
aq;
va_copy(aq,
ap);
...
va_end(aq);
下面是详细说明的英文,大家参考看看(你可以在
linux
系统了的
man
3 stdarg
手册里看到)。
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 noth‐
ing
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 arguments are passed in registers, it may be
necessary for va_start() to allocate memory, store the arguments
there, and also an indication
of
which argument 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 invocation
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.
相关文章推荐
- 用C语言实现参数个数可变的函数【转自中国程序员网】
- 【C语言天天练(四)】可变参数函数
- C语言中可变参数函数实现原理
- C语言中可变参数的用法
- c语言可变参数
- C语言中可变参数的用法
- C语言中可变参数的用法
- C语言中的可变参数的使用方法
- C语言中的可变参数。
- (转)c语言中可变参数函数的设计
- C语言可变参数函数执行原理以应用
- 【转载】C语言中如何使用宏 包括单双井号 可变参数
- C语言深入浅出可变参数函数的使用技巧
- C语言的可变参数
- C语言中可变参数的用法(ZZ)
- C语言关于可变参数函数的例子
- C语言中的可变参数函数的浅析(以Arm 程序中的printf()函数实现为例) . 分类: HI3531 arm-linux-Ubuntu 2013-12-16 14:19 438人阅读 评论(0) 收藏
- C语言深入浅出可变参数函数的使用技巧(转)
- C语言中可变参数的应用
- C语言可变参数全解