CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
不看会后悔的Windows XP之经验谈 简单快捷DIY实用家庭影院
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C语言

函数参数带省略号的用法

楼主jinweifu(浪萍飘影)2005-11-10 15:50:53 在 C/C++ / C语言 提问

这东西不太会用     请问怎么用       请解释一下      
  并且请举个简单例子       我只知道它是个不限参数 问题点数:50、回复次数:15Top

1 楼Caps77(厉兵秣马)回复于 2005-11-10 15:56:19 得分 10

水滴石穿C语言之可变参数问题    
  [日期:   2005-5-12   17:06:23]   来源:       作者:     [字体:大   中   小]      
   
  概述  
   
    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,则用来表明实际的参数个数,这为我们使用提供了进一步的方便,当然,这个参数不是必需的。  
   
    虽然参数没有固定形式,但我们必然要在函数中解析参数的意义,因此,理所当然会有一个要求,就是调用者和被调者之间要对参数区内容的格式,大小,有效性等所有方面达成一致,否则南辕北辙各说各话就惨了。  
   
    问题:可变长参数的传递  
   
    有时候,需要编写一个函数,将它的可变长参数直接传递给另外的函数,请问,这个要求能否实现?  
   
    答案与分析:  
   
    目前,你尚无办法直接做到这一点,但是我们可以迂回前进,首先,我们定义被调用函数的参数为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()来确定所有实际调用时可变长参数的类型和值。  
   
   
  Top

2 楼popy007(Twinsen)回复于 2005-11-10 17:25:49 得分 10

例子:把任意多个整数放入一个字符串中  
   
  #define   NUM_END   (-1000)  
  void   ShowNumbers(   int   first,   ...   )  
  {  
  char   str[MAX_PATH]   =   {'\0'};  
  char   num_str[8]   =   {'\0'};  
  int   i   =   0;  
  va_list   marker;  
   
  va_start(   marker,   first   );  
  i   =   first;  
  do  
  {  
  if(   i   !=   NUM_END   )  
  {  
  sprintf(num_str,   "   :%d",   i);  
  strcat(str,   num_str);  
  }  
  i   =   va_arg(   marker,   int   );  
  }while(i   !=   NUM_END);  
   
  va_end(   marker   );  
                    AfxMessageBox(str);  
  }  
  Top

3 楼popy007(Twinsen)回复于 2005-11-10 17:27:01 得分 0

对了,别忘了包含stdarg.hTop

4 楼Flood1984(峰子)回复于 2005-11-10 17:31:29 得分 5

回去多用printf练习练习.  
  Top

5 楼jinweifu(浪萍飘影)回复于 2005-11-12 18:57:29 得分 0

谢谢各位的回答     不过上面的宏有不明白的地方  
  #define   va_start(list)   list   =   (char*)&va_alist   /*是用list   指向va_alist吗?*/  
   
  在下面的程序段中:  
   
  #include   <stdarg.h>  
  int   printf(char*   format,   …)  
  {  
  va_list   ap;  
  va_start(ap,   format);  
  int   n   =   vprintf(format,   ap);/*   vprintf(format,ap)怎么在函数嵌套定义,好象违反了规则*/  
  va_end(ap);  
  return   n;  
  }Top

6 楼Flood1984(峰子)回复于 2005-11-14 12:33:43 得分 5

int   n   =   vprintf(format,   ap);/*   vprintf(format,ap)怎么在函数嵌套定义,好象违反了规则*/  
  C中是不能在这儿定义的.  
  必须把变量定义放在块的开头.Top

7 楼jixingzhong(瞌睡虫·星辰)回复于 2005-11-14 19:04:03 得分 5

#define   va_start(list)   list   =   (char*)&va_alist   /*是用list   指向va_alist吗?*/  
  ----------  
  84!  
   
  理解下   #define   吧   ~Top

8 楼yuanchuang(元创)回复于 2005-11-14 19:08:41 得分 0

MarkTop

9 楼avvcdwww()回复于 2005-11-14 23:43:20 得分 0

太深了,什么资料有这些内容啊?谢谢Top

10 楼jinweifu(浪萍飘影)回复于 2005-11-18 19:43:50 得分 0

To:jixingzhong(瞌睡虫:选择了远方,只顾风雨兼程!)    
  #define   va_start(list)   list   =   (char*)&va_alist/*二级指针,好象有点象*/  
   
  还有这边好象还有参数不匹配的问题  
  #define   va_start(list)   list   =   (char*)&va_alist  
   
  这边却出了两个参数  
  va_start(ap,   format);  
   
  Top

11 楼Mr_Yang(初级程序员)回复于 2005-11-18 21:54:33 得分 0

学习。Top

12 楼Kvci(看了不笑就没小JJ同时又比较长的昵称__——————————————————————————————)回复于 2005-11-18 21:58:45 得分 0

JFTop

13 楼ma100()回复于 2005-11-18 22:13:46 得分 5

还有一种情况   ISR   (中断服务程序)  
  在C可以这样写   void   interrupt   Comm1   ()  
  在c++   要写成   void   interrup     Comm1   (   ...   )  
  为什么我忘了Top

14 楼jinweifu(浪萍飘影)回复于 2005-11-19 11:06:20 得分 0

请看下面的资料,有一句不是很明白,就是这个宏     上面两个星号很难理解:  
  #define   va_arg(ap,t)         (   *(t   *)((ap   +=   _INTSIZEOF(t))   -   _INTSIZEOF(t))   )  
   
  编程入门:浅谈C语言的可变参数    
  作者:NeptuneX   更新时间:   2005-05-09            
       
    C语言中有些函数使用可变参数,比如常见的int   printf(   const   char*   format,   ...),第一个参数format是固定的,其余的参数的个数和类型都不固定。  
   
    C语言用va_start等宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。下面我们来分析这些宏。  
   
    在stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:  
   
   
   
   
      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(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。一般的sizeof(int)=4,也就是参数在内存中的地址都为4的倍数。比如,如果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。  
   
    为了能从固定参数依次得到每个可变参数,va_start,va_arg充分利用下面两点:  
   
    1.   C语言在函数调用时,先将最后一个参数压入栈  
   
    2.   X86平台下的内存分配顺序是从高地址内存到低地址内存  
   
    高位地址  
   
    第N个可变参数  
   
    。。。  
   
    第二个可变参数  
   
    第一个可变参数             ?   ap  
   
    固定参数                       ?   v  
   
    低位地址  
   
    由上图可见,v是固定参数在内存中的地址,在调用va_start后,ap指向第一个可变参数。这个宏的作用就是在v的内存地址上增加v所占的内存大小,这样就得到了第一个可变参数的地址。  
   
    接下来,可以这样设想,如果我能确定这个可变参数的类型,那么我就知道了它占用了多少内存,依葫芦画瓢,我就能得到下一个可变参数的地址。  
   
    让我再来看看va_arg,它先ap指向下一个可变参数,然后减去当前可变参数的大小即得到当前可变参数的内存地址,再做个类型转换,返回它的值。  
   
    要确定每个可变参数的类型,有两种做法,要么都是默认的类型,要么就在固定参数中包含足够的信息让程序可以确定每个可变参数的类型。比如,printf,程序通过分析format字符串就可以确定每个可变参数大类型。  
   
    最后一个宏就简单了,va_end使得ap不再指向有效的内存地址。  
   
    看了这几个宏,不禁让我再次感慨,C语言太灵活了,而且代码可以写得非常简洁,虽然有时候让人看得不是很明白,但是一旦明白   过来,你肯定会为它击掌叫好!  
   
    其实在varargs.h头文件中定义了UNIX   System   V实行的va系列宏,而上面在stdarg.h头文件中定义的是ANSI   C形式的宏,这两种宏是不兼容的,一般说来,我们应该使用ANSI   C形式的va宏。  
     
     
  Top

15 楼huangkou()回复于 2005-11-20 12:08:11 得分 10

看   c和指针,这本书上有Top

相关问题

  • 关于函数参数的省略号使用
  • 省略号参数函数怎么实现?
  • 在线等待,java函数的参数中可否向c++那样用省略号来代替
  • callback函数是怎么个调用法,参数怎么传?
  • 函数体参数out用法,请大家讲解一下!
  • 函数用法?
  • C++中函数列表里的省略号怎么用呀?
  • 如何在delphi的ICE中查看API函数的用法,原型,参数等
  • SQLConfigDataSource函数用法。
  • AnimateWindow函数用法

关键词

  • c++
  • 函数
  • 内存
  • 指针
  • 分析
  • 参数
  • va
  • 可变
  • argp
  • 定义

得分解答快速导航

  • 帖主:jinweifu
  • Caps77
  • popy007
  • Flood1984
  • Flood1984
  • jixingzhong
  • ma100
  • huangkou

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
Copyright © 2000-2008, CSDN.NET, All Rights Reserved
GongshangLogo