C语言中可变形参个数的函数实现

【@.1 可变形参个数函数】

C语言中,即使使用void*作为形参,也只能起到扩展形参类型的作用,而如果想要改变形参的个数呢?如果是C++的话就会很方便的通过函数的多态来实现这一功能,但是在C语言中实现起来就比较麻烦了。但并不是不可以实现,而是一般C编程没多少人用而已。考虑一个求平均数的函数,形参随便传入任意多个int类型的数,返回他们的平均值,用C语言应该怎么实现?这就需要用到可变形参个数的函数来实现了。

Variable Argument Lists, 在C语言中由stdarg.h头文件中定义了几个宏可以来做到这个事请。函数的形参无外乎是将他们以及函数的返回地址压入栈中,在运行时从栈中取出这些参数值,所以我们需要做的就是将传入的参数,不管有多少个,一个个从栈中找出来。

【@.2 求任意多个数的平均值函数】

float ave(int count,...)    //Need to specify the count of vars
{
    va_list valist;
    float sum=0;
    int i;
    va_start(valist,count);
    for(i=0;i<count;i++)
        sum+=va_arg(valist, int);
    va_end(valist);
    return sum/count;
}

以上设计一个函数求任意多个数的平均值,注意到形参列表的第二个为"..."三个英文句号。

首先va_list就是头文件<stdarg.h>中定义的一个类型,专门用于处理这类问题。宏va_start()的第一个参数我们本来应该在"..."中传入的形参,第二个count是形参的开始,也就是这个函数ave的第一个形参count。注意这第一个形参可以是任意类型,这里我们将它作为传入"..."的个数。之后使用一个for循环,用va_arg()宏将形参列表中的下一个,也就是从你调用ave()的第二个形参开始,一个个弹出。注意,这里并没用到i,也就是说,这个va_arg()用一次就从栈中弹出下一个形参,而且不会检查栈是否为空,所以调用的次数必须由程序员自己控制好。最后,在函数返回之前用va_end()将这个形参列表清空,最后返回值即可。

printf("average:%f\n",ave(5,    1,2,3,7,9));

使用时可以直接想上面一样printf打印出来就可以看到结果,非常方便。再次注意,第一个参数我们用做后面待求平均数的个数传入函数的。

好这里就出现问题了,我随便输入了多少个参数之后,还一定得输入这些参数的个数,太麻烦了,有没有一种检查机制能自动帮我判断输入参数的个数呢?答案是,没有。就像上面说到的那样,用va_arg()宏弹出形参栈的时候的次数必须由程序员自己保证,所以有多少参数这个统计工作得由程序员自己做。好在,我们可以采用一些技巧来帮助我们完成这一任务,比如下面代码:

float average(int count,...)    //Use an array to be the vars
{
    va_list valist;
    float sum=0;
    int i;
    int *array;
    va_start(valist,count);
    array=va_arg(valist, int*);
    for(i=0;i<count;i++)
    {
        sum+=array[i];
        printf("No.%2d:\t%d\n",i+1,array[i]);
    }

    va_end(valist);
    return sum/count;
}

int main(void) {
    int array[] = {5,7,9,11,13,15,20,22,16,11,-4,2,19};
    float result = average(sizeof(array)/sizeof(int),array);
    printf("Average: %f\n",result);
    //printf("average:%f\n",ave(5,    1,2,3,7,9));
    return EXIT_SUCCESS;
}

将想要求平均数的一组数放在int[]数组array里,使用时只需修改这个数组即可,参数的个数统计由sizeof函数就可以算出,比刚才方便多了。在统计函数里面采用va_arg时将形参作为一个int*弹出,再控制好大小一个个取出来即可。

【@.3 可变参数个数函数与C语言的多态】

C++语言的继承,封装,多态的特性完全可以结合C语言中的函数指针和结构体来完成。而可变参数个数的函数这种技巧也提供给我们用C的面向对象风格编程一条新的思路。比如这篇博客或者另一篇博客就提到了这一技巧,其中对象的实例化就用到了这里的可变参数函数技巧。

@.[FIN]      @.date->Jan 4, 2013      @.author->apollius

posted on 2013-01-04 23:02  apollius  阅读(1657)  评论(0编辑  收藏  举报

导航