变参函数的实现

rel="File-List" href="file:///C:%5CUsers%5CChendx%5CAppData%5CLocal%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CUsers%5CChendx%5CAppData%5CLocal%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CUsers%5CChendx%5CAppData%5CLocal%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

相对于固定参数的函数,变参函数的可用性无疑是更好的。我们最常用的变参函数包括scanfprintf。刚刚接触到变参函数的时候,我觉得这太神奇了,它并不知道我要输入什么类型的数据,要输入多少个数据,却能完美地处理。其实,可变参数机制实现起来是相当容易的(在stdarg.h的基础上),而且,它的作用并没有想象中的那么神奇。

         可变参数机制并不能获取某次输入的所有参数的个数,也不能自己确定每一个输入参数的类型。嗯,没错,看上去printfscanf就能知道每次输入的参数个数和每个参数的类型。其实,仔细想一想就会发现printfscanf没这个本事,输入的参数个数和每个参数的类型是使用者在format内容中,通过%模式等告诉编译器的。光是这么说可能不够取信于人,以简化的printf为例,让我们看一段K&R中的例程吧:

#include <stdarg.h>

/* minprintf: minimal printf with variable argument list */

void minprintf(char *fmt, ...)

{

    va_list ap; /* points to each unnamed arg in turn */

    char *p, *sval;

    int ival;

    double dval;

    va_start(ap, fmt); /* make ap point to 1st unnamed arg */

    for (p = fmt; *p; p++) {

       if (*p != '%') {

           putchar(*p);

           continue;

       }

       switch (*++p) {

           case 'd':

              ival = va_arg(ap, int);

              printf("%d", ival);

              break;

           case 'f':

              dval = va_arg(ap, double);

              printf("%f", dval);

              break;

           case 's':

              for (sval = va_arg(ap, char *); *sval; sval++)

                  putchar(*sval);

              break;

           default:

              putchar(*p);

              break;

       }

    }

    va_end(ap); /* clean up when done */

}

可以看出,printf处理可变参数的关键就在于它的参数char *fmt。注意程序中的for循环,指针p正是一次次地根据fmt中的提示(在这个简化的例子中是%)经由switch分支来确定下一个参数的类型和有效参数的个数。

         所以说,可变参数不是万能的,它只是一种很normal的机制,不过正是先驱们那化腐朽为神奇的想象力,借由这种normal的机制实现了神奇而又令人诟病的scanfprintf函数。一般而言,除了像printf一样,在函数中实现一个特殊的format之外,一些可变参数中的参数类型都是一致的(比如说只可能都是int)函数,则会在之前的固定参数中用一个参数指出可变参数的个数或是类型。下面是一个C Primer Plus中的例程:

#include <stdarg.h>

double sum(int lim,...)

{

    va_list ap;                   // declare object to hold arguments

    double tot = 0;

    int i;

    va_start(ap, lim);            // initialize ap to argument list

    for (i = 0; i < lim; i++)

       tot += va_arg(ap, double); // access each item in argument list

    va_end(ap);                   // clean up

    return tot;

}

可以看出,函数sum中的固定参数lim指出了可变参数的个数。

         以上介绍了可变参数机制实现的两种过程:自定义format和前置指示标志。接下来就会详细解释在C语言中如何具体实现可变参数机制。从两个例程可以看出,有四个宏是至关重要的:

va_list

va_start(va_list, lastpar)

va_arg(va_list, Type)

va_end(va_list)

va_list:一个char链表(实际上应该是一个连续的内存块,像数组一样),在使用时表现为一个指向char类型的指针;

va_start:初始化va_list。通过最后的固定参数实现对可变参数初始位置的定位,并为va_list分配内存,将可变参数复制该内存块中,使va_list指向该内存块的初始位置;

va_arg:通过移动指针va_list获取由参数Type指定的变量并返回该变量。

va_end:释放va_list拥有的内存块所占据的内存空间。

看,一切不就一清二楚了吗?不过,还有如下几个问题还需要特别注意一下:

1>     C标准规定实现可变参数机制的函数至少要有一个固定参数。从上面的讨论可以看出,这无论是从语法上还是实现上都是必须的。

2>     隐式类型转换不可用。比如说,va_arg指定了类型是double,若传入的是int的变量10就会出错。

3>     C语言的整型提升原则。也就是说,在va_arg中获取floatdouble使用的Type都是double,而获取charshortint使用的Type都是int

函数指针不可用,除非用 typedef 定义过。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值