C语言里的可变参数
来源:互联网 发布:wp embed.min.js 编辑:程序博客网 时间:2024/05/23 16:30
原创文章,转载请注明出处,谢谢!
作者:清林,博客名:飞空静渡
在c语言中有一种函数,它可以有可变参数,即是说它的参数的个数是不确定的。一个最典型的函数就是printf函数。其实我们自己也可以定义我们的可变参数的函数。
在udev的源代码里,我们也可以看到有可变参数的应用,在libudev.c源文件中有以下代码。
这是一个写log的函数,其中就用到了可变参数。
下面我们就来说一下可变参数的用法。
先看下使用可变参数都有哪几个宏。
在c89中定义的有va_start(),va_arg(), 和va_end()这三个宏,在c99中又增加了一va_copy()的宏。
在上面的代码中,我们已经看到了这几个洪的使用,下面我将用一个官方的例子来说明这几个宏的使用。下面是一个官方的例子代码:
下面我将一个一个的说明。
首先,看一下这几个宏的用法:
voidva_start(va_list ap, last);
typeva_arg(va_list ap, type);
voidva_end(va_list ap);
voidva_copy(va_list dest, va_list src);
我们在使用任何的可变参数时,都必须先声明一个va_list变量,而且是在va_start之前声明,如上面的va_listap。在声明完这个变量后就可以使用这几个宏了。
voidva_start(va_list ap, last);
va_start用来初始化我们刚才声明的ap变量,last就是我们的可变参数的前一个参数,如上面的代码中的 va_start(ap,fmt),fmt就是我们可变参数...前的参数。在调用va_start初始化ap后,我们才可以使用va_arg和va_end这两个宏。在使用va_start后ap会指向我们参数栈中的可变参数中的第一个参数(我们的函数的参数是放在栈里的,后面用图详细说明)。
typeva_arg(va_list ap, type);
va_arg的作用是取可变参数里的一个参数。ap是我们va_start中初始化中的ap,tyep是由我们自己指定的一个类型,在调用va_arg后,会返回可变参数中的一个参数,并会使ap指向下一个参数。我们来看下上面的代码:
由上面的程序我们可以知道,如果fmt的值是s的话,那么它后面的第一个参数是一个字符串指针,我们将会取得这个字符串并赋给s变量并打印出来;如果fmt的值是d的话,那么它后面的第一个参数是一个整型值,我们就把这个值赋给变量d并打印出来等等。
我们可以看到,va_arg的第二个参数的类型就返回值的类型,我们为什么要用这个参数呢?我们看下图,下图foo函数的栈结构。
假设我们的可变参数是intn,charc;即我们的函数
void
foo(char*fmt, …)
是这样的
void
foo(char*fmt, int n, char c)
那么当我们使用va_start(ap,fmt)时,我们的ap就指向intn,当我们调用intn1=va_arg(ap,int)时,我们的n1就等于n,而我们需要int这个参数是因为我们要知道n的大小,这样我们的ap就可以指向下一个产生c了,即在调用va_arg(ap,int)后,ap指向c了,这样下次我们就可以方便的取c的值。如果intn的类型出错,例如我们这样shorts=va_arg(ap,short)或者根本没有intn这个参数,即后面已经没有参数了,而我们还调用va_arg来获取参数,那么我们就会得到一个不确定的错误的。
voidva_end(va_list ap);
任何调用了va_start后,都必须要调用va_end来清除ap(ap的内部实现为一个指针)。
需要注意的一点是,va_start的宏的实现里带有{括号,而va_end的实现里带有}括号,所以这两个宏必须成对出现,而且只能出现在同一个函数里。
voidva_copy(va_list dest, va_list src);
一般我们会这样赋值:
va_listaq = ap;
或者
va_listaq;
*aq= *ap;
我们看到我们前面的函数的参数是放在栈里的,但是有些系统的函数的参数是放在寄存器里的,因此c99就定义了这个va_copy。
va_listaq;
va_copy(aq,ap);
...
va_end(aq);
下面是详细说明的英文,大家参考看看(你可以在linux系统了的man3 stdarg手册里看到)。
va_copy()
An obvious implementation would have a va_list be a pointer to thestack frame of the variadic function. In such a setup (by far themost common) there seems noth‐
ingagainst an assignment
va_listaq = ap;
Unfortunately,there are also systems that make it an array of pointers (of length1), and there one needs
va_listaq;
*aq= *ap;
Finally,on systems where arguments are passed in registers, it may benecessary for va_start() to allocate memory, store the argumentsthere, 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 accommodatethis situation, C99 adds a
macrova_copy(), so that the above assignment can be replaced by
va_listaq;
va_copy(aq,ap);
...
va_end(aq);
Eachinvocation of va_copy() must be matched by a corresponding invocationof va_end() in the same function. Some systems that do not supply va_copy() have
__va_copyinstead, 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语言可变参数
- C语言可变参数
- C语言可变参数
- C语言可变参数
- 人工智能:用AIML写一个机器人
- 没有花香,没有树高!
- 发表不了吗?
- PLSQL Developer使用技巧
- 当好项目经理经验谈
- C语言里的可变参数
- 谷歌竟然跟美第一夫人卯上了,就为张猩猩照.
- 采用offline的方式更改数据文件位置
- 091126周四
- 不错的博客
- 电子五所复试
- 读取 XML类 (XmlHelper)
- jbpm 3.2.2+Tomcat 6.0.10+MySQL5.0.27/SQL Server2000部署详解
- SQL中获得EXEC后面的sql语句或者存储过程的返回值的方法