• 关于C中函数的可变参数va_list...(转)


    ◎用法:
    func( Type para1, Type para2, Type para3, ... )
    {
          /****** Step 1 ******/
          va_list ap;
          va_start( ap, para3 ); //一定要“...”之前的那个参数
       
          /****** Step 2 ******/
          //此时ap指向第一个可变参数
          //调用va_arg取得里面的值
       
          Type xx = va_arg( ap, Type );
       
          //Type一定要相同,如:
          //char *p = va_arg( ap, char *);
          //int i = va_arg( ap, int );

          //如果有多个参数继续调用va_arg

          /****** Step 3 ******/
          va_end(ap); //For robust!
    }

    ◎研究:
    typedef char *    va_list;

    #define va_start _crt_va_start
    #define va_arg _crt_va_arg
    #define va_end _crt_va_end

    #define _crt_va_start(ap,v)    ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
    #define _crt_va_arg(ap,t)      ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    #define _crt_va_end(ap)        ( ap = (va_list)0 )
    va_list argptr;
    C语言的函数是从右向左压入堆栈的,调用va_start后,
    按定义的宏运算,_ADDRESSOF得到v所在的地址,然后这个
    地址加上v的大小,则使ap指向第一个可变参数如图:    
       
        栈底 高地址
        | .......    
        | 函数返回地址
        | .......     
        | 函数最后一个参数
        | ....                      
        | 函数第一个可变参数       <--va_start后ap指向
        | 函数最后一个固定参数
        | 函数第一个固定参数
        栈顶 低地址
        
       
    然后,用va_arg()取得类型t的可变参数值, 先是让ap指向下一个参数:
    ap += _INTSIZEOF(t),然后在减去_INTSIZEOF(t),使得表达式结果为
    ap之前的值,即当前需要得到的参数的地址,强制转换成指向此参数的
    类型的指针,然后用*取值

    最后,用va_end(ap),给ap初始化,保持健壮性。

    example:(chenguiming)

    #include    <stdio.h>  
       #include    <ctype.h>  
       #include<stdlib.h>  
       #include    <stdarg.h>  
       
       int    average(    int    first,    ...    )      //变参数函数,C++里也有  
       {  
             int    count=0,i=first,sum=0;  
             va_list    maker;            //va_list    类型数据可以保存函数的所有参数,做为一个列表一样保存  
             va_start(maker,first);    //设置列表的起始位置  
             while(i!=-1)  
             {  
             sum+=i;  
             count++;  
             i=va_arg(maker,int);//返回maker列表的当前值,并指向列表的下一个位置  
             }  
             return    sum/count;  
             
       }  
       
       void    main(void)  
       {  
       printf(    "Average    is:    %d\n",    average(    2,    3,    4,4,    -1    )    );  
       }

    先来个简单的例子:
    #include <stdio.h>
    #include <stdarg.h>
    int sum(int num,...);
    int sum(int num,...)
    {
     int result = 0;
     va_list argptr;
     va_start(argptr, num);
     while(num--)
     {
      //result += va_arg(argptr, int);
      printf("%s ",va_arg(argptr, char *));
     }
     va_end(argptr);
     return result;
    }
    int main()
    {
     sum(3, "hello", "world", "!");       // output: hello world !
     //printf("%d\n", sum(4, 1, 2, 3 ,4));
     //printf("%d\n", sum(2, 1, 2, 3 ,4));
     return 0;
    }

           可变参数中个数不定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。
           可变参数是由宏实现的,但是由于硬件平台的不同,编译器的不同,宏的定义也不相同,下面是VC6.0中x86平台的定义:

    typedef char * va_list;     // TC中定义为void*
    #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 )

          C语言的函数形参是从右向左压入堆栈的,以保证栈顶是第一个参数,而且x86平台内存分配顺序是从高地址到低地址。因此似函数fun(int var1,int var2,...,int varN)内存分配大致上是这样的:(可变参数在中间)
    栈区:
    |栈顶             低地址
    |第一个参数var1                  <-- &v
    |第二个参数var2                  <-- va_start(ap,v)后ap指向地址      
    |...
    |函数的最后varN
    |...
    |函数的返回地址
    |...
    |栈底    高地址
    va_start(ap,v);后ap = (va_list)&v + _INTSIZEOF(v)指向第二个参数地址
    调用va_arg(ap,t)  ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数

    不过被定义为宏的东西用起来要小心,我现在用不着va_list,不过先了解点皮毛也好。

    下面是msdn中的例子:

    #include <stdio.h>
    #define ANSI            /* Comment out for UNIX version     */
    #ifdef ANSI             /* ANSI compatible version          */
    #include <stdarg.h>
    int average( int first, ... );
    #else                   /* UNIX compatible version          */
    #include <varargs.h>
    int average( va_list );
    #endif

    void main( void )
    {
       /* Call with 3 integers (-1 is used as terminator). */
       printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );

       /* Call with 4 integers. */
       printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );

       /* Call with just -1 terminator. */
       printf( "Average is: %d\n", average( -1 ) );
    }

    /* Returns the average of a variable list of integers. */
    #ifdef ANSI             /* ANSI compatible version    */
    int average( int first, ... )
    {
       int count = 0, sum = 0, i = first;
       va_list marker;

       va_start( marker, first );     /* Initialize variable arguments. */
       while( i != -1 )
       {
          sum += i;
          count++;
          i = va_arg( marker, int);
       }
       va_end( marker );              /* Reset variable arguments.      */
       return( sum ? (sum / count) : 0 );
    }
    #else       /* UNIX compatible version must use old-style definition.  */
    int average( va_alist )
    va_dcl
    {
       int i, count, sum;
       va_list marker;

       va_start( marker );            /* Initialize variable arguments. */
       for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )
          sum += i;
       va_end( marker );              /* Reset variable arguments.      */
       return( sum ? (sum / count) : 0 );
    }
    #endif


    在来一个简单的例子:
    #include <stdio.h>
    #include <stdarg.h>

    void print(char *format,...);    //自定义输出格式
    void print(char *format,...)
    {
     va_list argptr;
     va_start(argptr, format);
     while(*format != '\0')
     {
      switch(*(format++))
      {
      case 's': printf("%s ", va_arg(argptr, char *)); break;
      case 'i': printf("%d ", va_arg(argptr, int)); break;
      case 'c': printf("%c ", va_arg(argptr, char)); break;
      case 'f': printf("%.1f\n", va_arg(argptr, double)); break;
      default: break;
      }
     }
     va_end(argptr);
    }
    int main()
    {
     print("sicft","laomou",24,'M',120.0);    // 输出格式依次为 string, integer, char, float
     return 0;
    }

     

  • 相关阅读:
    linux安装jdk
    maven工程直接部署在tomcat上
    将maven工程转成dynamic web project
    史上最全最强SpringMVC详细示例实战教程
    06-spring框架—— Spring 与Web
    05-spring框架—— Spring 事务
    04-spring框架—— Spring 集成 MyBatis
    03-spring框架—— AOP 面向切面编程
    01-spring框架——spring概述
    我对于语言只是工具的理解和感悟
  • 原文地址:https://www.cnblogs.com/AndyGe/p/1563372.html
Copyright © 2020-2023  润新知