
函数占用的是静态内存,因此需要提前告诉编译器占用内存大小,即调用时需要分配的栈空间,编译器主要是通过函数声明中描述的函数参数的个数以及类型,来计算参数所占用的栈空间。
既然是这样,那动态参数又是怎么实现的呢?C调用约定规定:由调用者把参数以从右向左的顺序压入栈中,并且由调用者清理堆栈中的参数,比如printf(char format, arg1, arg2, ),既然是调用者压入的参数,调用者知道栈中压入的参数情况,因此传入参数个数编译器在编译阶段就能确定下来。
栈中的参数个数其实都是在格式化字符串中,格式化字符串中有多少个'%',那么就在栈中找多少次参数。
在gcc的头文件stdargh中定义了与函数的可变参数相关的3个宏va_start、va_end、va_arg,
va_list用于存取可变参数。va_start用来初始化va_list变量。va_arg用来获取下一个可变参数。va_end用来反初始化va_list变量。va_arg函数根据当前给定的类型来取得当前指向的函数参数,并且递增内部的一个指针指向下一个参数。
比如
va_arg(arguments, char)这里假设当前参数是char类型,并修改内部指针,递增sizeof(char)指向下一个参数。获取的值可能是指向"Beijing"或者"Olympic Games"的指针。
va_arg(arguments, int)假设当前参数是int类型,并修改内部指针,递增sizeof(int)指向下一个参数。这里可能的值是2000(2个都是2000)。
注意,这是个非常低级的函数,在调用va_arg时指定的参数类型需要和传递时的一致,否则可能crash。还有需要注意的是,由于传递给这种函数时,char等类型会转化为int,所以在调用va_arg时必须使用int而不是char来提取参数,详见>
开发者们都知道在高端智能手机系统中有两种应用程序:一种是基于本地( *** 作系统)运行的APP;一种是基于高端机的浏览器运行的WebApp,本文将主要讲解后者。 WebApp与Native App有何区别呢? Native App: 1、开发成本非常大
一楼说的那个比较不沾边,楼主不要看了。
设计可变参数数目的函数的主要难度在于要自己手动从栈中提取参数,但这些有现成的宏定义可以用,在<stdargh>和<stdargsh>中。
下面是一个例子,sum()的功能是任意数量的整数求和。
sum()第一个参数是要求和的数的个数,后面跟着若干整型参数。如果实参个数小于第一个参数num的数值,sum()行为不确定。
也可以不用num而设置一个参数结束标志(比如最后一个参数为-1等),但也有同样的问题。说白了就是你必须给函数一个知道参数个数的方法,否则函数无法知道你到底在调用的时候给了几个实参。
#include <stdioh>
#include <stdargh>
int sum(int num, )
{
va_list parg;
int arg;
int s;
int i;
va_start(parg, num);
for (s = 0, i = 0; i < num; i++)
{
arg = va_arg(parg, int);
s += arg;
}
va_end(parg);
return s;
}
int main(int argc, char argv[])
{
printf("%d\n", sum(1, 1)); // 1
printf("%d\n", sum(2, 1, 2)); // 1 + 2
printf("%d\n", sum(3, 1, 2, 3)); // 1 + 2 + 3
printf("%d\n", sum(4, 1, 2, 3, 4)); // 1 + 2 + 3 + 4
return 0;
}
宏va_arg()、va_start()和va_end()一起使用,便可以完成向函数传入数目可变的变元 *** 作。取可变数目变元的典型例子是函数printf()。
类型va_list是在<stdargh>中定义的,上述的宏原型如下所示:
type va_arg(va_list argptr, type);
void va_end(va_list argptr);
void va_start(va_list argptr, last_parm);
它们都包含在头文件<stdargh>中。
创建一个能获取可变数目变元的函数的通用过程:在函数定义中,可变参数表之前必须有一个或多个已知参数,其中最右者为last_parm。在调用va_start()时,last_parm名被用作第二个参数。
使用任何可变长度的变元被访问之前,必须先用a_start()初始化变元指针argptr。初始化argptr后,经过对va_arg()的调用,以作为下一个参数类型的参数类型,返回参数。最后取完所有参数并从函数返回之前。必须调用a_end(),由此确保堆栈的正确恢复,如果未正确使用va_end(),程序可能瘫痪。
例子:
用sum_series()求一系列的总和,sum_series()的第一个变元是数列项目数。本程序是求以下数列的前四项之和:
1/2+1/(22)+1/(222)
#include <stdioh>
#include <stdargh>
double sum_series(int num, );
int main()
{
double d;
d= sum_series(4, 05, 025, 0125, 006254);
printf("Sum of series is %f\n", d);
return 0;
}
double sum_series(int num, )
{
double sum= 00, t;
va_list argptr;
va_start(argptr, num);
for(; num; num--)
{
t= va_arg(argptr, double);
sum= sum+ t;
}
va_end(argptr);
return sum;
}
这个是我贴过来的,但是有例子,我相信你能看明白了
一、为什么要使用可变参数的函数?
一般我们编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数。但在某些情况下希望函数的参数个数可以根据需要确定,因此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等。之所以这么做,是为了增加程序的可移植性。屏蔽不同的硬件平台造成的差异。
支持可变参数函数的所有宏都定义在stdargh 和 varargsh中。例如标准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的定义稍有不同。
在varargsh中,va_dcl的定义后面已经包括了一个分号。因此函数实现的时候,va_dcl后不再需要加上分号了。
3、采用头文件stdargh编写的程序是符合ANSI标准的,可以在各种 *** 作系统和硬件上运行;而采用头文件varargsh的方式仅仅是为了与以前的程序兼容,两种方式的基本原理是一致的,只是在语法形式上有一些细微的区别。 所以一般编程的时候使用stdargh下面的所有例子代码都采用ANSI标准格式。
四、可变参数函数的基本使用方法
下面通过若干例子,说明如何实现可变参数函数的定义和调用。
//================================ 例子程序1 ===============
#i nclude stdioh >
#i nclude stringh >
#i nclude stdargh >
/ 函数原型声明,至少需要一个确定的参数,注意括号内的省略号 /
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;
}
c++怎样编写不确定参数个数的函数
C++定义的函数是可以支持函数参数个数不确定的。VA_LIST是在C++语言中解决变参问题的一组宏,所在头文件:#include <stdargh>,用于获取不确定个数的参数同时使用代替多个参数,调用时只需要根据需要传入多个参数。
VA_LIST的用法:
首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
然后用VA_START宏初始化刚定义的VA_LIST变量;
然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
最后用VA_END宏结束可变参数的获取。
参考代码:求多个数得平均值
#include <cstdarg>#include <iostream>
using namespace std;
double average ( int num, )
{
va_list arguments; // A place to store the list of arguments
double sum = 0;
va_start ( arguments, num ); // Initializing arguments to store all values after num
for ( int x = 0; x < num; x++ ) // Loop until all numbers are added
sum += va_arg ( arguments, double ); // Adds the next value in argument list to sum
va_end ( arguments ); // Cleans up the list
return sum / num; // Returns some number (typecast prevents truncation)
}
int main()
{
cout<< average ( 3, 122, 223, 45 ) <<endl;
cout<< average ( 5, 33, 22, 11, 55, 33 ) <<endl;
}
以上就是关于可变参数原理全部的内容,包括:可变参数原理、关于C++的可变参数问题。、如何显示可变参数的数量 ios等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)