如何在一个可变参数函数中调用另一个可变参数函数 (如何在一个可爱的小跑车上装无极电机)
本文目录导航:
如何在一个可变参数函数中调用另一个可变参数函数
无方法间接做到这一点,不过可以驳回其余方法成功,例如:首先,定义被调用函数的参数为va_list类型,同时在调用函数中将可变长参数列表转换为va_list,这样就可以启动变长参数的传递了。看如下所示: void subfunc (char *fmt, va_list argp){ = 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);...}这样就OK了!
可变参函数的可变参函数的成功
C编译器理论提供了一系列解决这种状况的宏,以屏蔽不同的配件平台形成的差异,参与程序的可移植性。
这些宏包含va—start、va—arg和va—end等。
经常使用这些宏有两种不同的方式,二者在程序中包含的头文件不同,宏的定义也存在一些差异。
这两种方式对应的头文件和宏的申明见表1。
驳回ANSI规范方式时,参数个数可变的函数的原型申明是:type funcname(type para1, type para2, ...)这种方式至少须要一个普通的方式参数,前面的省略号不示意省略,而是函数原型的一局部,type是函数前往值和方式参数的类型。
驳回与UNIX System V兼容的申明方式时,参数个数可变的函数原型是:type funcname(va—alist)va—dcl这种方式不须要提供任何普通的方式参数,type是函数前往值的类型。
va—dcl是对函数原型申明中参数va—alist的详细申明,实践是一个宏定义,对不同的配件平台驳回不同的类型来定义,但在最后都包含了一个分号,因此va—dcl后不再须要加上分号了。
va—dcl在代码中必定原样给出,va—alist在VC中可以原样给出,也可以略去,但在UNIX上的CC或Linux上的GCC中都要省略掉。
此外,驳回头文件stdarg.h编写的程序是合乎ANSI规范的,可以在各种操作系统和配件上运转,而驳回头文件varargs.h的方式仅仅是为了与以前的程序兼容。
所以倡导大家经常使用前者。
两种方式的基本原理是分歧的,只是在语法方式上有一些纤细的区别。
以下重要就前一种方式对参数的解决做出说明。
va—start使argp指向第一个可选参数。
va—arg前往参数列表中的以后参数并使argp指向参数列表中的下一个参数。
va—end把argp指针清为NULL。
函数体内可以屡次遍历这些参数,然而都必定以va—start开局,并以va—end开头。
调用者在实践调用参数个数可变的函数时,要经过必定的方法指明实践参数的个数,例如把最后一个参数置为空字符串(系统调用execl()就是这样的)、-1或其余的方式。
上方给出一个详细的例子,前一局部是驳回了合乎ANSI规范方式的代码,后一局部是驳回了与UNIX System V兼容方式的代码。
代码中加了一些注释,这里就不再解释了。
该例子曾经在VC/Windows NT4.0、CC/AIX4.3.2.0、GCC/Redhat Linux 6.0环境下编译并反常运转。
1.展示如何经常使用参数个数可变的函数,驳回ANSI规范方式#include 〈stdio.h〉#include 〈string.h〉#include 〈stdarg.h〉/* 函数原型申明,至少须要一个确定的参数,留意括号内的省略号*/int demo( char*, ... );void main( void ){demo(″DEMO″, ″This″, ″is″, ″a″, ″demo!″, ″″);}/* ANSI规范方式的申明方式,括号内的省略号示意可选参数 */int demo( char *msg, ... ){va—list argp;/* 定义保留函数参数的结构 */int argno = 0; /* 纪录参数个数 */char *para;/* 寄存取出的字符串参数*//* argp指向传入的第一个可选参数,msg是最后一个确定的参数 */va—start( argp, msg );while (1) {para = va—arg( argp, char ?);/* 取出以后的参数,类型为char *. */if ( strcmp( para, ″″) == 0 )/* 驳回空串批示参数输入完结 */break;printf(″Parameter #%d is: %s\n″, argno, para);argno++;}va_end( argp ); /* 将argp置为NULL*/return 0;}
c/c++允许可变参数的函数
一、为什么要经常使用可变参数的函数?普通咱们编程的时刻,函数中方式参数的数目理论是确定的,在调用时要依次给出与方式参数对应的一实际践参数。
但在某些状况下宿愿函数的参数个数可以依据须要确定,因此c言语引入可变参数函数。
这也是c配置弱小的一个方面,其它某些言语,比如fortran就没有这个配置。
典型的可变参数函数的例子有大家相熟的printf()、scanf()等。
二、c/c++如何成功可变参数的函数?为了允许可变参数函数,C言语引入新的调用协定, 即C言语调用商定 __cdecl . 驳回C/C++言语编程的时刻,自动经常使用这个调用商定。
假设要驳回其它调用商定,必定参与其它关键字申明,例如WIN32 API经常使用PASCAL调用商定,函数名字之前必定加__stdcall关键字。
驳回C调用商定时,函数的参数是从右到左入栈,个数可变。
由于函数体不能预先知道传出去的参数个数,因此驳回本商定时必定由函数调用者担任堆栈清算。
举个例子://C调用商定函数int __cdecl Add(int a, int b){return (a + b);}函数调用:Add(1, 2);//汇编代码是:push 2 ;参数b入栈push 1 ;参数a入栈call @Add;调用函数。
其实还有编译器用于定位函数的表白式这里把它省略了addesp,8 ;调用者担任清栈假设调用函数的时刻经常使用的调用协定和函数原型中申明的不分歧,就会造成栈失误,这是另外一个话题,这里不再细说。
另外c/c++编译器驳回宏的方式允许可变参数函数。
这些宏包含va_start、va_arg和va_end等。
之所以这么做,是为了参与程序的可移植性。
屏蔽不同的配件平台形成的差异。
允许可变参数函数的一切宏都定义在stdarg.h 和 varargs.h中。
例如规范ANSI方式下,这些宏的定义是:typedef char * va_list; //字符串指针#define _INTSIZEOF(n)( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )#define va_end(ap) ( ap = (va_list)0 )经常使用宏_INTSIZEOF是为了依照整数字节对齐指针,由于c调用协定上方,参数入栈都是整数字节(指针或许值)。
三、如何定义这类的函数。
可变参数函数在不同的系统下,驳回不同的方式定义。
1、用ANSI规范方式时,参数个数可变的函数的原型申明是:type funcname(type para1, type para2, ……);对于这个定义,有三点须要说明:普通来说,这种方式至少须要一个普通的方式参数,可变参数就是经过三个.来定义的。
所以……不示意省略,而是函数原型的一局部。
type是函数前往值和方式参数的类型。
例如:int MyPrintf(char const* fmt, ……);然而,咱们也可以这样定义函数:void MyFunc(……);然而,这样的话,咱们就不可经常使用函数的参数了,由于不可经过上方所讲的宏来提取每个参数。
所以除非你的函数代码中确实没有用到参数表中的任何参数,否则必定在参数表中经常使用至少一个普通参数。
留意,可变参数只能位于函数参数表的最后。
不能这样:void MyFunc(……, int i);2、驳回与UNIX 兼容系统下的申明方式时,参数个数可变的函数原型是:type funcname(va_alist);然而要求函数成功的时刻,函数名字前面必定加上va_dcl.例如:#i nclude int average( va_list );void main( void ){。
。
。
//代码}/* UNIX兼容方式*/int average( va_alist )va_dcl{。
。
。
//代码}这种方式不须要提供任何普通的方式参数。
type是函数前往值的类型。
va_dcl是对函数原型申明中参数va_alist的详细申明,实践是一个宏定义。
依据平台的不同,va_dcl的定义稍有不同。
在varargs.h中,va_dcl的定义前面曾经包含了一个分号。
因此函数成功的时刻,va_dcl后不再须要加上分号了。
3、驳回头文件stdarg.h编写的程序是合乎ANSI规范的,可以在各种操作系统和配件上运转;而驳回头文件varargs.h的方式仅仅是为了与以前的程序兼容,两种方式的基本原理是分歧的,只是在语法方式上有一些纤细的区别。
所以普通编程的时刻经常使用stdarg.h.上方的一切例子代码都驳回ANSI规范格局。
四、可变参数函数的基本经常使用方法上方经过若干例子,说明如何成功可变参数函数的定义和调用。
//================================ 例子程序1 ===============#i ncludestdio.h >#i ncludestring.h >#i ncludestdarg.h >/* 函数原型申明,至少须要一个确定的参数,留意括号内的省略号 */ int demo( char *, ... ); void main( void ) { demo( DEMO, This, is, a, demo!, \0);} int demo( char *msg, ... ) { va_list argp; /* 定义保留函数参数的结构 */ int argno = 0; /* 纪录参数个数 */ char *para; /* 寄存取出的字符串参数 */ // 经常使用宏va_start, 使argp指向传入的第一个可选参数, // 留意 msg是参数表中最后一个确定的参数,并非参数表中第一个参数 va_start( argp, msg ); while (1) { //取出以后的参数,类型为char * //假设不给出正确的类型,将获取失误的参数 para = va_arg( argp, char *); if ( strcmp( para, \0) == 0 ) /* 驳回空串批示参数输入完结 */break; printf( 参数 #%d 是: %s\n, argno, para);argno++; } va_end( argp ); /* 将argp置为NULL */ return 0; }