标题:如何在C语言中定义参数数目可变的函数
只看楼主
zbqf109
Rank: 1
等 级:新手上路
帖 子:289
专家分:0
注 册:2006-12-31
 问题点数:0 回复次数:4 
如何在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;
}


[[italic] 本帖最后由 zbqf109 于 2007-12-31 01:45 编辑 [/italic]]
搜索更多相关主题的帖子: C语言 函数 参数 定义 可变 
2007-12-31 01:43
灭火的风
Rank: 2
来 自:杭州
等 级:论坛游民
帖 子:161
专家分:10
注 册:2006-6-15
得分:0 
平时好像用到不多,不过挺有意思的,谢谢楼主的奉献。《The C programming language》里也有比较清楚的说明,这本书比谭老的书好多了,而且貌似谭老的书有部分内容是从此书中搬去的....也难怪,不然这本书怎么被称为The holy bible of the C language呢?
2007-12-31 01:50
zbqf109
Rank: 1
等 级:新手上路
帖 子:289
专家分:0
注 册:2006-12-31
得分:0 
回复 2# 的帖子
哦?呵呵,很久没有好好读书了。《The C programming language》没有看过,自从三年前学C,到现在没有完整的读过一本书。我总是喜欢实用主义,所以不可避免就急功近利,这些东西都是基本的知识,有本好的教材真是好啊,否则都不知道去哪里找。
本来想分析一下GCC和VC对于va_宏的处理的区别,不过也没时间了。以后有机会再说吧,呵呵,欢迎提意见。

坚决不跟用TC的人打交道!
2007-12-31 01:57
灭火的风
Rank: 2
来 自:杭州
等 级:论坛游民
帖 子:161
专家分:10
注 册:2006-6-15
得分:0 
这本书被誉为C语言圣经....其实也言过其实了,因为现在真正的圣经已经是google了,呵呵
2007-12-31 02:04
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
得分:0 
可变参数最近我钻研了点点.
你看下面代码

int sum(int x,...)
{
        int temp=0;
        int* next_x=&x;
        while(*next_x>0)
        {
                temp=temp+(*next_x);
                next_x++;
        }
        return temp;
}

你找一下函数参数在内存的表示法就可能就会明白的了.
我对这个不定参数的东西也还不大的了解,最近正在钻研
当中.有什么好东西发上来共享共享.
2007-12-31 09:18



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-195211-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.306253 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved