-
可变参数
2018-06-13 09:27:06//方法的可变参数//前提:方法参数数据类型确定,参数的个数任意//可变参数,本质就是一个数组public class VarArgumentsDemo { public static void main(String[] args) { //getSum(); int sum= getSum(1,4,35,6...//方法的可变参数
//前提:方法参数数据类型确定,参数的个数任意
//可变参数,本质就是一个数组
public class VarArgumentsDemo {
public static void main(String[] args) {
//getSum();
int sum= getSum(1,4,35,6,7,8,6);
System.out.println(sum);
}
//定义方法,计算2个整数和
public static int getSum(int a,int b){
return a+b;
}
//可变参数
public static int getSum(int...a){
int sum=0;
for(int i:a){
sum=sum+i;
}
return sum;
}
}/*
*可变参数的注意事项
*1.一个方法中,可变参数只能有一个
*2.可变参数必须写在参数列表的最后一位 public static void function(int a,int b,int...c){}
*/ -
可变参数
2010-05-31 20:33:00本文介绍可变参数用法及其本质。1) 典型例子
printf(“ %d, %d, %d”, a, b,c )
int Func( int a, …) ; //C++用法
int Func(); //C用法
2) 用法
l include <stdarg.h>
l va_start(va_list, 前一个参数变量)
#define char* va_list
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
取得变量在堆栈中的地址
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
以上使得大小是4的倍数
#define va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
l va_arg(va_list, 要取的参数类型)
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
l va_end(va_list)
#define _crt_va_end(ap) ( ap = (va_list)0 )
注意:1)至少需要指定第一个参数,因为va_start需要它定位
堆栈中参数的地址;
2) 一般来说,可以考虑利用多态来替代可变参数。
3) 具体使用
int customarg( int a, ...)
{
va_list arg_ptr;
va_start(arg_ptr, a);
int b = va_arg(arg_ptr, int);
int c = va_arg(arg_ptr, int);
long d = va_arg(arg_ptr, long);
char* p = va_arg(arg_ptr, char*);
double e = va_arg(arg_ptr,double);
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
cout<<"d = "<<d<<endl;
cout<<"p = "<<p<<endl;
cout<<"e = "<<e<<endl;
va_end(arg_ptr);
return a;
}
customarg1(2,33,4444444);
customarg(2,33,4444444,777777777,"hello",443.3);
可见:
1)用可变参数时,需要有一个地方指出参数的个数,一般就
用第一个参数。
2)在堆栈中每个变量其实是按4个字节对齐的(入栈出栈是按
字的),否则定位的时候就会出错,从_INTSIZEOF宏的定
义可以看出。
4) 类似printf的使用
不用自己解析可变参数:
l void Function( const string format, ... )
{
va_list args;
static const int nBUFFERSIZE = 1024;
char szTempBuffer[ nBUFFERSIZE ];
va_start( args, format);
StringCchVPrintf( szTempBuffer, nBUFFERSIZE, format.c_str(), args );
cout<<szTempBuffer<<endl;
}
l Function(“Hello %s, your age is %d/n”, “Ming”, 29);
-
可变参数及可变参数宏的使用
2018-06-12 09:15:19可变参数的宏一般在调试打印Debug 信息的时候, 需要可变参数的宏. 从C99开始可以使编译器标准支持可变参数宏(variadic macros), 另外GCC 也支持可变参数宏, 但是两种在细节上可能存在区别.1. __VA_ARGS____VA_ARGS_...我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,这里将介绍可变函数的写法以及原理.
* 1. 可变参数的宏
一般在调试打印Debug 信息的时候, 需要可变参数的宏. 从C99开始可以使编译器标准支持可变参数宏(variadic macros), 另外GCC 也支持可变参数宏, 但是两种在细节上可能存在区别.
1. __VA_ARGS__
__VA_ARGS__ 将"..." 传递给宏.如
#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)
在GCC中也支持这类表示, 但是在G++ 中不支持这个表示.
2. GCC 的复杂宏
GCC使用一种不同的语法从而可以使你可以给可变参数一个名字,如同其它参数一样。
#define debug(format, args...) fprintf (stderr, format, args)
这和上面举的那个定义的宏例子是完全一样的,但是这么写可读性更强并且更容易进行描述。
3. ##__VA_ARGS__
上面两个定义的宏, 如果出现debug("A Message") 的时候, 由于宏展开后有个多余的逗号, 所以将导致编译错误. 为了解决这个问题,CPP使用一个特殊的‘##’操作。
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
这里,如果可变参数被忽略或为空,‘##’操作将使预处理器(preprocessor)去除掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,GNU CPP也会工作正常,它会把这些可变参数放到逗号的后面。
4. 其他方法
一种流行的技巧是用一个单独的用括弧括起来的的 "参数" 定义和调用宏, 参数在宏扩展的时候成为类似 printf() 那样的函数的整个参数列表。
#define DEBUG(args) (printf("DEBUG: "), printf(args))* 2. 可变参数的函数
写可变参数的C函数要在程序中用到以下这些宏:
void va_start( va_list arg_ptr, prev_param )
type va_arg( va_list arg_ptr, type )
void va_end( va_list arg_ptr )
va在这里是variable-argument(可变参数)的意思,这些宏定义在stdarg.h中.下面我们写一个简单的可变参数的函数,该函数至少有一个整数参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.- void simple_va_fun(int i, ...)
- {
- va_list arg_ptr;
- int j=0;
- va_start(arg_ptr, i);
- j=va_arg(arg_ptr, int);
- va_end(arg_ptr);
- printf("%d %d\n", i, j);
- return;
- }
在程序中可以这样调用:- simple_va_fun(100);
- simple_va_fun(100,200);
从这个函数的实现可以看到,使用可变参数应该有以下步骤:
1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针.
2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个参数是你要返回的参数的类型,这里是int型.
4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获取各个参数.
如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)simple_va_fun(100);
结果是:100 -123456789(会变的值)
2)simple_va_fun(100,200);
结果是:100 200
3)simple_va_fun(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果的原因和可变参数在编译器中是如何处理的.* 3. 可变参数函数原理
va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于硬件平台的不同,编译器的不同,所以定义的宏也有所不同,下面以VC++中stdarg.h里x86平台的宏定义摘录如下:定义_INTSIZEOF(n)主要是为了内存对齐,C语言的函数是从右向左压入堆栈的(设数据进栈方向为从高地址向低地址发展,即首先压入的数据在高地址). 下图是函数的参数在堆栈中的分布位置:- 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 )
低地址 |-----------------------------|<-- &v
|第n-1个参数(最后一个固定参数)|
|-----------------------------|<--va_start后ap指向
|第n个参数(第一个可变参数) |
|-----------------------------|
|....... |
|-----------------------------|
|函数返回地址 |
高地址 |-----------------------------|
1. va_list 被定义为char *
2. va_start 将地址ap定义为 &v+_INTSIZEOF(v),而&v是固定参数在堆栈的地址,所以va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址
3. va_arg 取得类型t的可变参数值,以int型为例,va_arg取int型的返回值:
j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
4. va_end 使ap不再指向堆栈,而是跟NULL一样.这样编译器不会为va_end产生代码.
在不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.* 4. 小结
对于可变参数的函数,因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数需要在该函数中由程序代码控制;另外,编译器对可变参数的函数的原型检查不够严格,对编程查错不利.
所以我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,无需用到可变参数.如果在C++里,我们应该利用C++的多态性来实现可变参数的功能,尽量避免用C语言的方式来实现.* 5. 附一些代码
- #define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)
- #define debug(format, args...) fprintf (stderr, format, args)
- #define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
// 使用va... 实现- void debug(const char *fmt, ...)
- {
- int nBuf;
- char szBuffer[1024];
- va_list args;
- va_start(args, fmt);
- nBuf = vsprintf(szBuffer, fmt, args) ;
- assert(nBuf >= 0);
- printf("QDOGC ERROR:%s\n",szBuffer);
- va_end(args);
- }
-
Java可变参数
2020-08-01 19:26:04目录 1可变参数【应用】 2可变参数的使用【应用】 ...如果一个方法有多个参数,包含可变参数,可变参数要放在最后 可变参数的基本使用 public class ArgsDemo01 { public static void ma...目录
1 可变参数【应用】
可变参数介绍可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了可变参数定义格式修饰符 返回值类型 方法名(数据类型… 变量名) { }可变参数的注意事项这里的变量其实是一个数组如果一个方法有多个参数,包含可变参数,可变参数要放在最后可变参数的基本使用public class ArgsDemo01 { public static void main(String[] args) { System.out.println(sum(10, 20)); System.out.println(sum(10, 20, 30)); System.out.println(sum(10, 20, 30, 40)); System.out.println(sum(10,20,30,40,50)); System.out.println(sum(10,20,30,40,50,60)); System.out.println(sum(10,20,30,40,50,60,70)); System.out.println(sum(10,20,30,40,50,60,70,80,90,100)); } // public static int sum(int b,int... a) { // return 0; // } public static int sum(int... a) { int sum = 0; for(int i : a) { sum += i; } return sum; } }
2 可变参数的使用【应用】
Arrays工具类中有一个静态方法:public static List asList(T... a):返回由指定数组支持的固定大小的列表返回的集合不能做增删操作,可以做修改操作List接口中有一个静态方法:public static List of(E... elements):返回包含任意数量元素的不可变列表返回的集合不能做增删改操作Set接口中有一个静态方法:public static Set of(E... elements) :返回一个包含任意数量元素的不可变集合在给元素的时候,不能给重复的元素返回的集合不能做增删操作,没有修改的方法示例代码public class ArgsDemo02 { public static void main(String[] args) { //返回由指定数组支持的固定大小的列表 public static <T> List<T> asList(T... a): List<String> list = Arrays.asList("hello", "world", "java"); list.add("javaee"); //UnsupportedOperationException list.remove("world"); //UnsupportedOperationException list.set(1,"javaee"); System.out.println(list); //返回包含任意数量元素的不可变列表 public static <E> List<E> of(E... elements): List<String> list = List.of("hello", "world", "java", "world"); list.add("javaee");//UnsupportedOperationException list.remove("java");//UnsupportedOperationException list.set(1,"javaee");//UnsupportedOperationException System.out.println(list); //返回一个包含任意数量元素的不可变集合 public static <E> Set<E> of(E... elements) : Set<String> set = Set.of("hello", "world", "java","world"); IllegalArgumentException Set<String> set = Set.of("hello", "world", "java"); set.add("javaee");//UnsupportedOperationException set.remove("world");//UnsupportedOperationException System.out.println(set); } }
-
Java可变参数/可变长参数
2016-10-20 10:52:32Java可变参数/可变长参数传递的参数不确定长度,是变长的参数,例如小例子:package demo; public class Demo { public static int sum(int n, int... nums) { for (int i = 0; i ; i++) { n = n + nums[i];... -
Java方法的可选参数 可变参数
2018-11-07 13:13:21Java方法的可选参数 可变参数 -
Python进阶(二十四)-Python中函数的参数定义和可变参数
2017-04-06 11:37:36Python进阶(二十四)-Python中函数的参数定义和可变参数 刚学用Python的时候,特别是看一些库的源码时,经常会看到func(*args, **kwargs)这样的函数定义,这个和*让人有点费解。其实只要把函数参数定义搞清楚了,... -
Java 参数可变参数
2018-04-25 22:51:00这并不是错误语法,而是Java的可变参数 可变参数既可以是没有参数(空参数),也可以是不定长的。不定长的参数其实和数组参数挺像的。事实上,也确实是这么回事儿。编译器会在悄悄地把这最后一个形参转化为一个数组... -
GO 可变参数 ...
2020-03-06 01:03:58GO 可变参数 …args & args… 文章目录GO 可变参数 ...args & args...1、内部实现2、使用3、函数间的传递参考 1、内部实现 可变参数 …Type 等效于 []Type 的切片,当 …Type 未传递数值时为 的切片 []Type,... -
C 可变参数
2019-03-15 21:47:29本文并无讲述可变参数的原理,只是简要介绍可变参数的宏和封装得到变参函数的方法。 -
C语言可变参数
2019-02-14 11:38:27C语言可变参数可以使用宏函数取出,宏函数在头文件stdarg.h中。 贴出如下简单的代码,博客转载自: https://www.cnblogs.com/edver/p/8419807.html 亦可参考:... -
Java可变参数关于参数列表含可变参数的方法重载的注意点
2016-01-21 23:50:35可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项。当可变参数个数多于一个时,必将有一个不是最后一项,所以只支持有一个可变参数。因为参数个数不定... -
可变参数的认识
2019-12-06 11:59:52可变参数是c语言函数中参数的个数和类型是不定的;具有可变参数的函数,称为可变参数函数。要注意的是,可变参数函数必须有一个以上的固定参数,可变参数必须作为最后一个函数参数。 ...代表的就是可变参数,将来... -
Python之函数中的位置可变参数和关键字可变参数
2019-06-01 10:35:001.位置可变参数( * ) 在形式参数名称前加一个星号 *,则代表使用该形式参数可以接收任意多个参数,而且接收的参数将会以元组的方式组织。 例: def func(*args): #arg形参以元组的方式组织传入的实参 print(args)... -
Python 缺省参数,可变参数
2019-04-16 20:33:39Python中形参有确定参数,缺省参数,可变参数,关键字参数。具体的使用和区别如下 确定参数:平时最常用的必传确定数量的参数即为确定参数 缺省参数:在调用函数时可以传也可以省去的参数,如果不传将使用默认值 ... -
Java 可变参数
2016-05-16 19:31:26Java1.5增加了新特性:可变参数 适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。 注意:可变参数必须位于最后一项。当可变参数个数多余一个时,必将有一个不是最后一项,所以只支持有一个可... -
Java可变参数以及一个简单应用
2019-06-13 22:46:42一、背景 写代码的时候接触到了Java可变... 可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。 注意:可变参数必须位于最后一项。 2.2 特点 只能出现在参数列表的最后; ...位... -
python的位置参数、默认参数、关键字参数、可变参数区别
2016-06-20 01:02:28下面让我们一起探讨python位置参数、默认参数、关键字参数、可变参数区别