ImageVerifierCode 换一换
格式:DOC , 页数:13 ,大小:37.50KB ,
资源ID:5872191      下载积分:10 金币
快捷注册下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/5872191.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请

   平台协调中心        【在线客服】        免费申请共赢上传

权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

注意事项

本文(如何实现参数个数可变的函数.doc)为本站上传会员【xrp****65】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

如何实现参数个数可变的函数.doc

1、如何实现参数个数可变的函数 参数个数不定的函数,最频繁使用的就是printf()与scanf()。其实,我们也可以自己实现这样的功能,首先看一个例子: #include #include int Sum(int first, int second, ...) {       int sum = 0, t = first;       va_list vl;       va_start(vl, first);       while (t != -1)       {              sum += t;        

2、      t = va_arg(vl, int);//将当前参数转换为int类型       }       va_end(vl);       return sum; } int main(int argc, char* argv[]) {       printf("The sum is %d\n", Sum(30, 20, 10, -1));//-1是参数结束标志       return 0; } 在上面的例子中,实现了一个参数个数不定的求int型和的函数Sum()。函数中一开始定义了一个va_list型变量vl,该变量用来访问可变参数,实际上就是指针,接着使用v

3、a_start使vl指向第一个参数,然后再使用va_arg来遍历每一个参数,va_arg返回参数列表中的当前参数并使vl指向参数列表中的下一个参数。最后通过va_end把vl指针清为NULL。在这里,va_start,va_arg,va_end其实都是宏,那么这些宏又是如何实现他们的功能的呢? 一 个很显然的问题就是既然参数个数在函数调用之前没有确定,所以,在函数定义的时候没有办法像普通函数那样使用确定的变量来接受传进来的参数,于是,问题的 关键就是如何接收到这些不确定的参数了?首先,看看函数调用时实际发生的情况,在函数调用的时候,使用栈传递参数,拿上例来说,如果调用Sum(30, 20,

4、10, -1),那么参数入栈后的情形如下图所示(假定按照自右至左的顺序入栈): 既然参数在栈中的情形已经知道了,那么,如果使用指针(程序中的 vl)指向第一个参数(va_start(vl, first)),因为所有参数都是连续存放的,通过移动指针就可以访问到每一个参数了(va_arg(vl, int)),这就是我在程序中使用到那几个宏的实现思想。        明白了以上原理之后,就可以完全不使用宏,自己实现这样的一个功能了,下面的程序是我按照上面的思想改写的: #include int Sum(int first, int second, ...)

5、 { int sum = 0, t = first; char * vl;//定义一个指针 vl = (char *)&first;//使指针指向第一个参数 while (*vl != -1)//-1是预先给定的结束符 {         sum += *(int *)vl;//类型转换         vl += sizeof(int);//移动指针,使指针指向下一个参数 } return sum; } int main(int argc, char* argv[]) { printf("The sum is %d\n", Sum(30, 20, 10, -1));

6、//-1是参数结束标志 return 0; } 可以看出,通过使用指针的确实现了参数个数不定的函数了,但是程序中还有一个问题,那就是移动指针,在程序中因为我使用的参数都是相同的int类型,所以可以事先知道访问下一个参数的时候应该移动sizeof(int)个字节,但是,如果参数的类型不同呢?这的确是个比较麻烦的问题,因为不同的数据类型占用的字节数可能是不一样的(如double型为8个字符,short int型为2个),所以很难事先确定应该移动多少个字节!但是办法还是有的,这就是使用指针了,无论什么类型的指针,都是占用4个字节,所以,可以把所有的传人参数都设置为指针,这样一来,就可以通过移动

7、固定的4个字节来实现遍历可变参数的目的了,至于如果取得指针中的内容并使用他们,当然也是无法预先得知的了,所以这大概也就是像printf(),scanf()之类的函数还需要一个格式控制符的原因吧^_^!不过实现起来还是有不少麻烦,暂且盗用vprintf()来实现一个与printf()函数一样功能的函数了,代码如下: void myPrint(const char *frm, ...) {     va_list vl;     va_start(vl, frm);     vprintf(frm, vl);     va_end(vl); } 如何用C实现参数个数可变的函数 采

8、用C语言编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数。但在某些情况下希望函数的参数个数可 以根据需要确定。典型的例子有大家熟悉的函数printf()、scanf()和系统调用execl()等。那么它们是怎样实现的呢?   C编译器通常提供了一系列处理这种情况的宏,以屏蔽不同的硬件平台造成的差异,增加程序的可移植性。这些宏包括va—start、va—arg和va—end等。   使用这些宏有两种不同的形式,二者在程序中包括的头文件不同,宏的定义也存在一些差别。这两种方式对应的头文件和宏的声明见表1。   采用ANSI标准形式

9、时,参数个数可变的函数的原型声明是:   type funcname(type para1, type para2, ...)   这种形式至少需要一个普通的形式参数,后面的省略号不表示省略,而是函数原型的一部分,type是函数返回值和形式参数的类型。   采用与UNIX System V兼容的声明方式时,参数个数可变的函数原型是:   type funcname(va—alist)   va—dcl   这种形式不需要提供任何普通的形式参数,type是函数返回值的类型。va—dcl是对函数原型声明中参数va—alist的详细声明,实际是一个宏

10、定义,对不同的硬件平台采用不同的类型来定义,但在最后都包括了一个分号,因此va—dcl后不再需要加上分号了。va—dcl在代码中必须原样给 出,va—alist在VC中可以原样给出,也可以略去,但在UNIX上的CC或Linux上的GCC中都要省略掉。此外,采用头文件stdarg.h编 写的程序是符合ANSI标准的,可以在各种操作系统和硬件上运行,而采用头文件varargs.h的方式仅仅是为了与以前的程序兼容。所以建议大家使用前 者。两种方式的基本原理是一致的,只是在语法形式上有一些细微的区别。以下主要就前一种方式对参数的处理做出说明。   va—start使argp指向第一个可选参数。

11、va—arg返回参数列表中的当前参数并使argp指向参数列表中的下一个参数。va—end把argp指针清为NULL。函数体内可以多次遍历这些参数,但是都必须以va—start开始,并以va—end结尾。   调用者在实际调用参数个数可变的函数时,要通过一定的方法指明实际参数的个数,例如把最后一个参数置为空字符串(系统调用execl()就是这样的)、-1或其他的方式。   下面给出一个具体的例子,前一部分是采用了符合ANSI标准形式的代码,后一部分是采用了与UNIX System V兼容方式的代码。代码中加了一些注释,这里就不再解释了。该例子已经在VC/Windows NT4.0

12、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″

13、 ″demo!″, ″ ″);   }   / ANSI标准形式的声明方式,括号内的省略号表示可选参数 /   int demo( char msg, ... )   {   va—list argp;   / 定义保存函数参数的结构 /   int argno = 0; / 纪录参数个数 /   char para;   / 存放取出的字符串参数 /   / argp指向传入的第一个可选参数,msg是最后一个确定的参数 /   va—start( argp, msg );

14、   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

15、0;   }   2.演示如何使用参数个数可变的函数,采用与UNIX System V兼容的方式   #include 〈stdio.h〉   #include 〈string.h〉   #include 〈varargs.h〉   / 函数原型声明,括号内的类型va—list在VC/Windows NT4.0可以保留,但在AIX和Linux下需要去掉,即改成int demo( ) /   int demo( va—list );   void main( void )   {    demo(″This″,

16、″is″, ″a″, ″demo!″, ″ ″);   }   / UNIX System V采用的声明方式,括号内是va—alist,不是va—list,而且va—dcl后面不需要分号 /   int demo( va—alist )   va—dcl     {   va—list argp;    / 定义保存函数参数的结构 /      int argno = 0; / 纪录参数个数 /      char para;   / 存放取出的字符串参数 /   va—start( a

17、rgp );   / argp指向第一个可选参数 /   while (1) {   para = va—arg( argp, char );   / 取出当前的参数,类型为char /      if ( strcmp( para, ″ ″) == 0 )   / 采用空串指示参数输入结束 /      break;      printf(″Parameter #%d is: %s\n″, argno, para);      argno++;      }   va_end( arg

18、p ); / 将argp置为NULL /   return 0;   } c/c++支持可变参数的函数 一、为什么要使用可变参数的函数?    一般我们编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数。但在某些情况下希望函数的参数个数可以根据需 要确定,因此c语言引入可变参数函数。这也是c功能强大的一个方面,其它某些语言,比如fortran就没有这个功能。    典型的可变参数函数的例子有大家熟悉的printf()、scanf()等。    二、c/c++如何实现可变参数的函数?    为了支持可变参数函数,C语言引入

19、新的调用协议, 即C语言调用约定 __cdecl . 采用C/C++语言编程的时候,默认使用这个调用约定。如果要采用其它调用约定,必须添加其它关键字声明,例如WIN32 API使用PASCAL调用约定,函数名字之前必须加__stdcall关键字。    采用C调用约定时,函数的参数是从右到左入栈,个数可变。由于函数体不能预先知道传进来的参数个数,因此采用本约定时必须由函数调用者负责堆栈清理。举个例子:  //C调用约定函数    int __cdecl Add(int a, int b)    {       return (a + b);    }    函数调用:    A

20、dd(1, 2);    //汇编代码是:    push        2            ;参数b入栈    push        1            ;参数a入栈    call        @Add         ;调用函数。其实还有编译器用于定位函数的表达式这里把它省略了    add         esp,8        ;调用者负责清栈    如果调用函数的时候使用的调用协议和函数原型中声明的不一致,就会导致栈错误,这是另外一个话题,这里不再细说。      支持可变参数函数的所有宏都定义在stdarg.h 和 varargs.h中。例如标准

21、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 = (

22、va_list)0 )    使用宏_INTSIZEOF是为了按照整数字节对齐指针,因为c调用协议下面,参数入栈都是整数字节(指针或者值)。    三、如何定义这类的函数。    可变参数函数在不同的系统下,采用不同的形式定义。    1、用ANSI标准形式时,参数个数可变的函数的原型声明是:    type funcname(type para1, type para2, ……);    关于这个定义,有三点需要说明:    一般来说,这种形式至少需要一个普通的形式参数,可变参数就是通过三个'.'来定义的。所以"……"不表示省略,而是函数原型的一部分。type是函数返回值和形式

23、参数的类型。    例如:    int MyPrintf(char const* fmt, ……);    但是,我们也可以这样定义函数:    void MyFunc(……);    但是,这样的话,我们就无法使用函数的参数了,因为无法通过上面所讲的宏来提取每个参数。所以除非你的函数代码中的确没有用到参数表中的任何参数,否则必须在参数表中使用至少一个普通参数。   注意,可变参数只能位于函数参数表的最后。不能这样:    void MyFunc(……, int i);    2、采用与UNIX 兼容系统下的声明方式时,参数个数可变的函数原型是:    type funcn

24、ame(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的定义稍有

25、不同。    在varargs.h中,va_dcl的定义后面已经包括了一个分号。因此函数实现的时候,va_dcl后不再需要加上分号了。    3、采用头文件stdarg.h编写的程序是符合ANSI标准的,可以在各种操作系统和硬件上运行;而采用头文件varargs.h的方式仅仅是为了与以 前的程序兼容,两种方式的基本原理是一致的,只是在语法形式上有一些细微的区别。 所以一般编程的时候使用stdarg.h.下面的所有例子代码都采用ANSI标准格式。   四、可变参数函数的基本使用方法    下面通过若干例子,说明如何实现可变参数函数的定义和调用。  //=================

26、 例子程序1 =============== #i nclude < stdio.h > #i nclude < string.h > #i nclude < stdarg.h > /* 函数原型声明,至少需要一个确定的参数,注意括号内的省略号 */ int demo( char *, ... ); void main( void ) { demo("DEMO", "This", "is", "a", "demo!", "\0"); } int demo( char *msg, ... ) { va_list argp; /* 定义保存函数

27、参数的结构 */     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") ==

28、0 ) /* 采用空串指示参数输入结束 */    break; printf("参数 #%d 是: %s\n", argno, para);      argno++;     } va_end( argp ); /* 将argp置为NULL */     return 0; } //输出结果 参数 #0 是: This 参数 #1 是: is 参数 #2 是: a 参数 #3 是: demo! 注意到上面的例子没有使用第一个参数,下面的例子将使用所有参数 //================================ 例子程序2 =============

29、 #i nclude #i nclude int average( int first, ... ); //输入若干整数,求它们的平均值 void main( void ) {    /* 调用3个整数(-1表示结尾) */    printf( "Average is: %d\n", average(2,3,4, -1));    /*调用4个整数*/    printf( "Average is: %d\n", average(5,7,9, 11,-1));    /*只有结束符的调用*/    printf( "Aver

30、age is: %d\n", average(-1) ); } /* 返回若干整数平均值的函数 */ int average( int first, ... ) {    int count = 0, sum = 0, i = first;    va_list marker;    va_start( marker, first ); //初始化    while( i != -1 )    {       sum += i; //先加第一个参数       count++;       i = va_arg( marker, int);//取下一个参数    }

31、    va_end( marker );    return( sum ? (sum / count) : 0 ); } //输出结果 Average is: 3 Average is: 8 Average is: 0    五、关于可变参数的传递问题    有人问到这个问题,假如我定义了一个可变参数函数,在这个函数内部又要调用其它可变参数函数,那么如何传递参数呢?上面的例子都是使用宏va_arg逐个把参数提取出来使用,能否不提取,直接把它们传递给另外的函数呢?    我们先看printf的实现:  int __cdecl printf (const char *form

32、at, ...) {         va_list arglist;         int buffing;         int retval;         va_start(arglist, format); //arglist指向format后面的第一个参数 ...//不关心其它代码         retval = _output(stdout,format,arglist); //把format格式和参数传递给output函数 ...//不关心其它代码         return(retval); }    我们先模仿这个函数写一个:  #i nc

33、lude #i nclude int mywrite(char *fmt, ...) {      va_list arglist;      va_start(arglist, fmt);      return printf(fmt,arglist); } void main() { int i=10, j=20; char buf[] = "This is a test"; double f= 12.345; mywrite("String: %s\nInt: %d, %d\nFloat :%4.2f\n", buf,

34、i, j, f); }    运行一下看看,哈,错误百出。仔细分析原因,根据宏的定义我们知道 arglist是一个指针,它指向第一个可变的参数,但是所有的参数都位于栈中,所以arglist指向栈中某个位置,通过arglist的值,我们可以直接查看栈里面的内容:    arglist -> 指向栈里面,内容包括    0067FD78 E0 FD 67 00 //指向字符串"This is a test"    0067FD7C 0A 00 00 00 //整数 i 的值    0067FD80 14 00 00 00 //整数 j 的值    0067FD84 71 3D 0A

35、D7 //double 变量 f, 占用8个字节    0067FD88 A3 B0 28 40    0067FD8C 00 00 00 00    如果直接调用 printf(fmt, arglist); 仅仅是把arglist指针的值0067FD78入栈,然后把格式字符串入栈,相当于调用:    printf(fmt, 0067FD78);    自然这样的调用肯定会出现错误。    我们能不能逐个把参数提取出来,再传递给其它函数呢?先考虑一次性把所有参数传递进去的问题。    如果调用的是系统库函数,这种情况下是不可能的。因为提取参数是在运行态,而参数入栈是在编译的时候确定的。无法让编译器预知运行态的事情给出正确的参数 入栈代码。而我们在运行态虽然可以提取每个参数,但是无法将参数一次性全部压栈,即使使用汇编代码实现起来也是很困难的,因为不单是一个简单的push代 码就可以做到。

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服