可变参数传递的方法

本文介绍了三种实现可变参数传递的方法:使用va_list处理可变参数、利用参数入栈原理获取参数以及使用数组传递参数。这些方法适用于需要处理不确定数量参数的函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 可变参数传递方法
        ---written by andyhua

 

       有时候,我们写函数的时候,可能遇到参数数量不确定的情况,这样的函数例如C的printf打印函数。你可以使用printf("some string"), 也可以使用printf("%d", aintvalue)。是不是C的库里包含了不同定义的printf函数呢?答案肯定是否定的,因为C是不支持函数的重载的。每个函数只能由一个版本。那么是如何实现类似这种可变参数传递的函数呢?在这讲三种方法。

(1)方法1:使用va_list
          涉及到如下变量和函数(摘自msdn):
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
void va_start( va_list arg_ptr );   (UNIX version)
void va_start( va_list arg_ptr, prev_param );   (ANSI version)
         参数含义:
type:参数类型
arg_ptr:指向参数列表的指针
prev_param:第一个参数的类型

         下面采用msdn里的一个例子。

  1. /* VA.C: The program below illustrates passing a variable
  2.  * number of arguments using the following macros:
  3.  *      va_start            va_arg              va_end
  4.  *      va_list             va_dcl (UNIX only)
  5.  */
  6. #include <stdio.h>
  7. #define ANSI            /* Comment out for UNIX version     */
  8. #ifdef ANSI             /* ANSI compatible version          */
  9. #include <stdarg.h>
  10. int average( int first, ... );
  11. #else                   /* UNIX compatible version          */
  12. #include <varargs.h>
  13. int average( va_list );
  14. #endif
  15. void main( void )
  16. {
  17.    /* Call with 3 integers (-1 is used as terminator). */
  18.    printf( "Average is: %d/n", average( 2, 3, 4, -1 ) );
  19.    /* Call with 4 integers. */
  20.    printf( "Average is: %d/n", average( 5, 7, 9, 11, -1 ) );
  21.    /* Call with just -1 terminator. */
  22.    printf( "Average is: %d/n", average( -1 ) );
  23. }
  24. /* Returns the average of a variable list of integers. */
  25. #ifdef ANSI             /* ANSI compatible version    */
  26. int average( int first, ... )
  27. {
  28.    int count = 0, sum = 0, i = first;
  29.    va_list marker;
  30.    va_start( marker, first );     /* Initialize variable arguments. */
  31.    while( i != -1 )
  32.    {
  33.       sum += i;
  34.       count++;
  35.       i = va_arg( marker, int);
  36.    }
  37.    va_end( marker );              /* Reset variable arguments.      */
  38.    return( sum ? (sum / count) : 0 );
  39. }
  40. #else       /* UNIX compatible version must use old-style definition.  */
  41. int average( va_alist )
  42. va_dcl
  43. {
  44.    int i, count, sum;
  45.    va_list marker;
  46.    va_start( marker );            /* Initialize variable arguments. */
  47.    for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )
  48.       sum += i;
  49.    va_end( marker );              /* Reset variable arguments.      */
  50.    return( sum ? (sum / count) : 0 );
  51. }
  52. #endif

          通过上面的例子可以清楚知道怎么传递可变参数了。

(2)方法2:利用参数入栈原理  
         在现有的32位的机器上,一般通过将参数按照由低地址到高地址的顺序依次入栈实现传递,因此把第一参数的指针依次增加,就可以得到后面的参数了。例如:

  1. struct s1 
  2. {
  3.     int a;
  4.     char b;
  5. };
  6. struct s2 
  7. {
  8.     int a;
  9. };
  10. void function (struct s1 a, ...) 
  11. {
  12.     struct s1*  pa = &a;
  13.     printf("%d, %c/n", pa->a, pa->b);
  14.     ++pa;
  15.     printf("%d, %c/n", pa->a, pa->b);
  16.     ++pa;
  17.     printf("%d/n", (struct s2*)pa->a);  //强制转换为struct s2类型
  18. }
  19. void main( void )
  20. {
  21.     struct s1 a1 = {1, 'A'};
  22.     struct s1 a2 = {2, 'B'};
  23.     struct s2 a3 = {10};
  24.     function(a1, a2, a3); //a3的类型不同于a1, a2
  25.     return;
  26. }

        用这种方法传递可变参数非常简单。


(3)方法3:用数组传参

 

  1. void   function(Type a[])   
  2. {   
  3.   ...   
  4.   a[0];    
  5.   a[1];   
  6.   ...   
  7. }

这种方法就不唠叨了,相信地球人都知道。       

### Java 中可变参数传递机制的使用与原理 #### 1. 可变参数的概念 Java 的可变参数(Varargs)是在 Java 5 引入的功能,它允许方法接受不定数量的参数。通过这种方式,开发者无需为不同数量的参数创建多个重载方法[^3]。 #### 2. 可变参数的语法结构 在方法签名中,可以通过在参数类型的后面添加省略号 `...` 来定义可变参数。其基本形式如下: ```java public void methodName(DataType... parameterName) { // 方法体 } ``` 这里的 `DataType` 是参数的数据类型,而 `parameterName` 则代表传入的一组值,在方法内部会被视为一个数组[^4]。 #### 3. 实际应用案例 下面展示如何利用 Varargs 处理动态输入数据的例子: ```java public class TestVarargs { public static void main(String[] args) { printNames("Alice"); printNames("Bob", "Charlie"); printNames(); } public static void printNames(String... names) { for (String name : names) { System.out.println(name); } } } ``` 在这个例子中,`printNames` 方法能够接收零个或多个字符串作为参数,并逐一打印它们。如果没有任何参数被传递,则不会有任何输出[^2]。 #### 4. 内部实现机制 尽管看起来像是直接支持了多参数调用,但实际上,编译器会将这些参数封装成一个数组对象再传递给目标函数。这意味着即使你在外部只提供了单个或者几个独立变量,进入实际执行阶段之后也会变成相应类型的数组实例[^1]。 例如上述代码片段中的 `method1` 接收的是 String 类型的 vararg 参数列表,但在运行期间实际上接收到的就是一个 `String[]` 数组。 #### 5. 注意事项 - **性能开销**:由于底层实现了基于数组的操作,所以可能会带来额外内存分配上的成本。 - **混合参数顺序**:如果希望同时拥有固定位置和其他可选参数的话,请注意保持固定的非-varargs 参数位于前面的位置上;否则会导致编译错误提示不明确的情况发生。 - **不可滥用**:虽然方便但是过度依赖可能导致程序逻辑变得难以理解和维护,尤其是在复杂场景下容易引发潜在问题比如空指针异常等风险增加[^5]。 #### 总结 综上所述,Java 提供了一种简洁优雅的方式来简化那些原本需要大量重复劳动才能完成的任务——即处理未知大小集合的能力。然而与此同时也要意识到它的局限性和最佳实践指导原则以便更好地运用这项技术优势于我们的日常开发工作中去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值