虫虫闹翻天公司:变参函数的实现 - 菜鸟的专栏 - CSDN博客

来源:百度文库 编辑:九乡新闻网 时间:2024/04/28 10:42:00

用户操作
[留言] [发消息] [加为好友]
dexingchenID:dexingchen
[编辑]dexingchen的公告
[编辑]文章分类
C/C++
Linux
精品软件
算法
杂类
装机买本
存档
2009年02月(3)
2009年01月(1)
2008年12月(10)
2008年11月(10)
[官方博客]
 变参函数的实现收藏
相对于固定参数的函数,变参函数的可用性无疑是更好的。我们最常用的变参函数包括scanf和printf。刚刚接触到变参函数的时候,我觉得这太神奇了,它并不知道我要输入什么类型的数据,要输入多少个数据,却能完美地处理。其实,可变参数机制实现起来是相当容易的(在stdarg.h的基础上),而且,它的作用并没有想象中的那么神奇。
可变参数机制并不能获取某次输入的所有参数的个数,也不能自己确定每一个输入参数的类型。嗯,没错,看上去printf和scanf就能知道每次输入的参数个数和每个参数的类型。其实,仔细想一想就会发现printf和scanf没这个本事,输入的参数个数和每个参数的类型是使用者在format内容中,通过%模式等告诉编译器的。光是这么说可能不够取信于人,以简化的printf为例,让我们看一段K&R中的例程吧:
#include
/* 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的机制实现了神奇而又令人诟病的scanf和printf函数。一般而言,除了像printf一样,在函数中实现一个特殊的format之外,一些可变参数中的参数类型都是一致的(比如说只可能都是int)函数,则会在之前的固定参数中用一个参数指出可变参数的个数或是类型。下面是一个C Primer Plus中的例程:
#include
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中获取float和double使用的Type都是double,而获取char、short和int使用的Type都是int。
函数指针不可用,除非用typedef定义过。发表于 @2008年11月29日 23:27:00 |评论( 0) |编辑|举报|收藏
旧一篇:typedef和#define | 新一篇:关键字class与struct
查看最新精华文章 请访问博客首页相关文章
一天一个C Run-Time Library 函数(3) abort
一天一个C Run-Time Library 函数(5) access
一天一个C Run-Time Library 函数 (7) asctime(时间函数)
一天一个C Run-Time Library 函数(8) assert
一天一个C Run-Time Library 函数 (9) atexit
tolua++参考手册(翻译二)tolua++类型、函数部分详解
将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
C++中的虚函数调用及RTTI原理的反汇编实例分析(2)