如何在C语言中定义参数数目可变的函数
提前声明:由于我对C的理解还不够深刻,所以这个东西里面可能会有些常识性的错误,欢迎各位不吝指出。希望这个东西对大家有所帮助。昨天晚上偶然看到一篇文章,讲到了C语言可变数目参数的函数。其实很早之前我就注意到<stdio.h>中的几个函数的参数个数是可变的,但一直不知道怎么实现。看了那篇文章,稍微有了点思路。
首先,文章介绍了几个宏――va_start, va_end, va_arg。我查了一下C99,其7.15节介绍的就是Variable Arguments<stdarg.h>(我比较奇怪的是, C99在这一节中的介绍顺序, va_list->va_arg->va_copy->va_end->va_start, 呵呵). 简单说一下这几个va_开头的东西的含义吧.
(1) va_list: typedef char* va_list
va_start: 声明为 void va_start ( va_list ap, parmN );
顾名思义, va_start要在所有其它的va_开头的宏前面最先使用(除了用va_list定义变量外), va_start的作用就是初始化ap, 因为后面的va_copy, va_arg, va_end都要使用到ap. C99说的很明白, 由va_start和va_end构成一个scope, 所以在一对va_start和va_end之间不能再次使用va_start. parmN 就是"..."之前的最后一个参数. 例如, printf函数是这样定义的: printf(const char *format, ...); 那么在printf函数中的va_start使用之后, parmN 的值就等于*format.
(2) va_arg: 声明为 type va_arg (va_list ap, type);
va_arg的作用就是返回参数列表ap中的下一个具有type类型的参数, 每次调用va_arg都会修改ap的值, 所以你才能"连续不断"的获取下一个type类型的参数. ap就是前面使用va_start初始化的ap.
(3) va_copy: 声明为 void va_copy (va_list dest, va_list src);
其作用是将src中的内容复制到dest中, dest成为src的一个副本.
(4) va_end: 声明为 void va_end (va_list ap);
va_end与va_start构成了一个scope, va_end之后ap就无效了.
其实C99的这些规定已经足够我们使用了. 让人欣慰的是, GCC提供了更加便捷有效的方式来处理这些va_开头的宏. 在GCC的<ansidecl.h>中定义了三个宏: VA_OPEN, VA_FIXEDARG, VA_CLOSE. 在注释中这样说:
/*
...
VA_OPEN and VA_CLOSE are used *instead of* va_start and va_end. Immediately after VA_OPEN, put a sequence of VA_FIXEDARG calls corresponding to the list of fixed arguments. Then use va_arg normally to get the variable arguments, or pass your va_list object around. You do not declare the va_list yourself; VA_OPEN does it for you.
...
You can declare variables either before or after the VA_OPEN, VA_FIXEDARG sequence. Also, VA_OPEN and VA_CLOSE are the beginning and end of a block. They must appear at the same nesting level, and any variables declared after VA_OPEN go out of scope at VA_CLOSE. Unfortunately, with a K+R compiler, that includes the argument list. You can have multiple instances of VA_OPEN/VA_CLOSE pairs in a single function in case you need to traverse the
argument list more than once.
...
*/
也就是说, 如果我们使用VA_OPEN和VA_CLOSE那么连定义va_list的变量也省了. 我们可以看一下VA_OPEN, VA_CLOSE, VA_FIXEDARG的定义:
#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP); { struct Qdmy
#define VA_CLOSE(AP) } va_end(AP); }
#define VA_FIXEDARG(AP, TYPE, NAME) TYPE NAME = va_arg(AP, TYPE)
我们可以根据需要来使用这些宏.
下面我们就来写一个例子, 编译环境DevCpp 4.9.9.2
程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <ansidecl.h>
#include <stdarg.h>
#define va_list char*
// 这个其实是GCC的<ansidecl.h>中的示例程序, 作用就是printf
int vary_args PARAMS ((const char *format, ...));
// 这个是我简单写的一个函数, 作用是求最大值
int max_v PARAMS ((int nValue, ...));
int main(int argc, char *argv[])
{
char str[] = "Hello, world. \n %s";
char str2[] = "World, hello.\n";
int nResult = vary_args(str, str2);
printf("nResult = %d\n", nResult);
int nMaxV = max_v(6, 5, 3, 4);
printf("nMaxV = %d\n", nMaxV);
system("pause");
return 0;
}
int vary_args VPARAMS ((const char *format, ...))
{
int result = 0;
VA_OPEN (ap, format);
VA_FIXEDARG(ap, const char *, format);
result = vfprintf(stdout, format, ap);
/* 关于vfprintf和stdout这里先不作说明, 有兴趣的可以查阅相关资料 */
VA_CLOSE(ap);
return result;
}
int max_v PARAMS ((int nValue, ...))
{
int result = 0, nTemp = 0;
VA_OPEN(ap, nValue);
result = nValue;
while (strcmp(ap, ""))
{
//VA_FIXEDARG(ap, int, nTemp);
nTemp = va_arg(ap, int);
if (nTemp > result)
result = nTemp;
}
VA_CLOSE(ap);
return result;
}
[code][/code]#include <stdlib.h>
#include <ansidecl.h>
#include <stdarg.h>
#define va_list char*
// 这个其实是GCC的<ansidecl.h>中的示例程序, 作用就是printf
int vary_args PARAMS ((const char *format, ...));
// 这个是我简单写的一个函数, 作用是求最大值
int max_v PARAMS ((int nValue, ...));
int main(int argc, char *argv[])
{
char str[] = "Hello, world. \n %s";
char str2[] = "World, hello.\n";
int nResult = vary_args(str, str2);
printf("nResult = %d\n", nResult);
int nMaxV = max_v(6, 5, 3, 4);
printf("nMaxV = %d\n", nMaxV);
system("pause");
return 0;
}
int vary_args VPARAMS ((const char *format, ...))
{
int result = 0;
VA_OPEN (ap, format);
VA_FIXEDARG(ap, const char *, format);
result = vfprintf(stdout, format, ap);
/* 关于vfprintf和stdout这里先不作说明, 有兴趣的可以查阅相关资料 */
VA_CLOSE(ap);
return result;
}
int max_v PARAMS ((int nValue, ...))
{
int result = 0, nTemp = 0;
VA_OPEN(ap, nValue);
result = nValue;
while (strcmp(ap, ""))
{
//VA_FIXEDARG(ap, int, nTemp);
nTemp = va_arg(ap, int);
if (nTemp > result)
result = nTemp;
}
VA_CLOSE(ap);
return result;
}
[[italic] 本帖最后由 zbqf109 于 2007-12-31 01:45 编辑 [/italic]]
----------------解决方案--------------------------------------------------------
平时好像用到不多,不过挺有意思的,谢谢楼主的奉献。《The C programming language》里也有比较清楚的说明,这本书比谭老的书好多了,而且貌似谭老的书有部分内容是从此书中搬去的....也难怪,不然这本书怎么被称为The holy bible of the C language呢?
----------------解决方案--------------------------------------------------------
回复 2# 的帖子
哦?呵呵,很久没有好好读书了。《The C programming language》没有看过,自从三年前学C,到现在没有完整的读过一本书。我总是喜欢实用主义,所以不可避免就急功近利,这些东西都是基本的知识,有本好的教材真是好啊,否则都不知道去哪里找。本来想分析一下GCC和VC对于va_宏的处理的区别,不过也没时间了。以后有机会再说吧,呵呵,欢迎提意见。
----------------解决方案--------------------------------------------------------
这本书被誉为C语言圣经....其实也言过其实了,因为现在真正的圣经已经是google了,呵呵
----------------解决方案--------------------------------------------------------
可变参数最近我钻研了点点.
你看下面代码
int sum(int x,...)
{
int temp=0;
int* next_x=&x;
while(*next_x>0)
{
temp=temp+(*next_x);
next_x++;
}
return temp;
}
你找一下函数参数在内存的表示法就可能就会明白的了.
我对这个不定参数的东西也还不大的了解,最近正在钻研
当中.有什么好东西发上来共享共享.
----------------解决方案--------------------------------------------------------