C语言之可变参数问题
副标题#e#
概述
C语言中有一种长度不确定的参数,形如:"…",它主要用在参数个数不确定的函数中,我们最容易想到的例子是printf函数。
原型:
int printf( const char *format [, argument]… );
利用例:
printf("Enjoy yourself everyday!\n");
printf("The value is %d!\n", value);
这种可变参数可以说是C语言一个较量难领略的部门,这里会由几个问题激发一些对它的阐明。
留意:在C++中有函数重载(overload)可以用来区别差异函数参数的挪用,但它照旧不能暗示任意数量的函数参数。
问题:printf的实现
请问,如何本身实现printf函数,如那里理惩罚个中的可变参数问题? 谜底与阐明:
在尺度C语言中界说了一个头文件<stdarg.h>专门用来搪塞可变参数列表,它包括了一组宏,和一个va_list的typedef声明。一个典范实现如下:
typedef char* va_list;
#define va_start(list) list = (char*)&va_alist
#define va_end(list)
#define va_arg(list, mode)\
((mode*) (list += sizeof(mode)))[-1]
本身实现printf:
#include <stdarg.h>
int printf(char* format, …)
{
va_list ap;
va_start(ap, format);
int n = vprintf(format, ap);
va_end(ap);
return n;
}
问题:运行时才确定的参数
有没有步伐写一个函数,这个函数参数的详细形式可以在运行时才确定?
谜底与阐明:
今朝没有"正规"的办理步伐,不外独门偏方倒是有一个,因为有一个函数已经给我们做出了这方面的模范,那就是main(),它的原型是:
int main(int argc,char *argv[]);
函数的参数是argc和argv。
深入想一下,"只能在运行时确定参数形式",也就是说你没步伐从声明中看到所接管的参数,也等于参数基础就没有牢靠的形式。常用的步伐是你可以通过界说一个void *范例的参数,用它来指向实际的参数区,然后在函数中按照按照需要任意表明它们的寄义。这就是main函数中argv的寄义,而argc,则用来表白实际的参数个数,这为我们利用提供了进一步的利便,虽然,这个参数不是必须的。
固然参数没有牢靠形式,但我们一定要在函数中理会参数的意义,因此,理所虽然会有一个要求,就是挪用者和被调者之间要对参数区内容的名目,巨细,有效性等所有方面告竣一致,不然背道而驰各说各话就惨了。
#p#副标题#e#
问题:可变长参数的通报
有时候,需要编写一个函数,将它的可变长参数直接通报给别的的函数,请问,这个要求可否实现?
谜底与阐明:
今朝,你尚无步伐直接做到这一点,可是我们可以迂回前进,首先,我们界说被挪用函数的参数为va_list范例,同时在挪用函数中将可变长参数列表转换为va_list,这样就可以举办变长参数的通报了。看如下所示:
void subfunc (char *fmt, va_list argp)
{
...
arg = va_arg (fmt, argp); /* 从argp中逐一取出所要的参数 */
...
}
void mainfunc (char *fmt, ...)
{
va_list argp;
va_start (argp, fmt); /* 将可变长参数转换为va_list */
subfunc (fmt, argp); /* 将va_list通报给子函数 */
va_end (argp);
...
}
问题:可变长参数中范例为函数指针
我想利用va_arg来提取出可变长参数中范例为函数指针的参数,功效却老是不正确,为什么?
谜底与阐明:
这个与va_arg的实现有关。一个简朴的、演示版的va_arg实现如下:
#define va_arg(argp, type) \
(*(type *)(((argp) += sizeof(type)) – sizeof(type)))
个中,argp的范例是char *。
假如你想用va_arg从可变参数列表中提取出函数指针范例的参数,譬喻
int (*)(),则va_arg(argp, int (*)())被扩展为:
(*(int (*)() *)(((argp) += sizeof (int (*)())) -sizeof (int (*)())))
显然,(int (*)() *)是无意义的。
办理这个问题的步伐是将函数指针用typedef界说成一个独立的数据范例,譬喻:
typedef int (*funcptr)();
这时候再挪用va_arg(argp, funcptr)将被扩展为:
(* (funcptr *)(((argp) += sizeof (funcptr)) – sizeof (funcptr)))
这样就可以通过编译查抄了。
问题:可变长参数的获取
有这样一个具有可变长参数的函数,个中有下列代码用来获取范例为float的实参:
va_arg (argp, float);
这样做可以吗?
谜底与阐明:
不行以。在可变长参数中,应用的是"加宽"原则。也就是float范例被扩展成double;char, short被扩展成int。因此,假如你要去可变长参数列表中本来为float范例的参数,需要用va_arg(argp, double)。对char和short范例的则用va_arg(argp, int)。
问题:界说可变长参数的一个限制
为什么我的编译器不答允我界说如下的函数,也就是可变长参数,可是没有任何的牢靠参数?
int f (...)
{
...
}
谜底与阐明:
不行以。这是ANSI C 所要求的,你至少得界说一个牢靠参数。
这个参数将被通报给va_start(),然后用va_arg()和va_end()来确定所有实际挪用时可变长参数的范例和值。