java va start_函数变参的使用

java va start_函数变参的使用介绍C/C++,java,和go语言对函数变参的使用。1.C/C++语言的变参1.1变参函数声明变参函数的声明typefunc_name(constchar*format,…)typefunc_name(intcount,…)说明变参是通过三个点号(…)来表示。变参不能是函数第一个参数,否则编译器会报错,例如t.c:4:12:error:ISOCrequir…

大家好,欢迎来到IT知识分享网。java va start_函数变参的使用

介绍C/C++,java,和go语言对函数变参的使用。

1. C/C++语言的变参

1.1 变参函数声明

变参函数的声明

type func_name(const char * format, …)

type func_name(int count, …)

说明

变参是通过三个点号(…)来表示。

变参不能是函数第一个参数,否则编译器会报错,例如

t.c:4:12: error: ISO C requires a named argument before ‘…’

变参必须是函数的最后一个参数

va_start的第二个参数要求是最后一个命名参数,否则:

t.c:11:4: warning: second parameter of ‘va_start’ not last named argument [-Wvarargs]

1.2 如何调用

这个很简单啦

func_name(“%s,%s,%d\n”, “AA”, “BB”, 33); // 变参个数为 3

func_name(“Hello\n”); // 变参个数为 0

func_name(1, 100) // 变参个数为 1

func_name(2, 100, 200) // 变参个数为 2

1.3 函数内如何分析变参

主要有三个相关的变量和宏,定义在 stdarg.h 头文件里。

// 定义va_list类型的变量

va_list valist;

// 初始化valist,第二个参数表示从这个参数之后的参数进入valist

// 这也正好符合前面的限制,变参不能是第一个参数。

va_start(valist, num);

// 依次访问所有的参数

// 其中第二个参数是类型,这依赖于应用程序自行保证类型的正确性。

for (i = 0; i < num; i++) {

sum += va_arg(valist, int);

}

// 清除valist

va_end(valist);

举个例子来说:

#include

#include

void showParam(char * types, …) {

int i;

char c;

char * s;

va_list valist;

char * p = types;

va_start(valist, types);

for(;*p != ‘\0’; p++) {

switch(*p) {

case ‘i’:

i = va_arg(valist, int);

printf( “%d\n”, i);

break;

case ‘c’:

c = va_arg(valist, int);

printf(“%c\n”, c);

break;

case ‘s’:

s = va_arg(valist, char *);

printf(“%s\n”, s);

break;

default:

break;

}

}

va_end( valist );

}

int main(int argc, char * argv[]) {

showParam(“ics”, 123,’A’,”abc”);

return 0;

}

运行结果如下:

$ gcc main.c && ./a.out

123

A

abc

1.4 一个变参函数如何调用另一变参函数

例如

void Log(char * level, const char * format, …) {

}

void Debug(const char * format, …) {

}

Debug函数如何调用Log呢?

void Debug(const char * format, …) {

Log(“DEBUG”, format, …)

}

这样写行吗,好像语法不通啊。

实际上C/C++不支持一个变参直接调用另一个变参函数。

但是可以把被调用的函数调整一下即可,看例子:

#include

#include

void Debug(const char* format, …)

{

char output[1024];

va_list valist;

va_start(valist, format);

vsprintf(output, format, valist);

va_end(valist);

printf(“%s\n”, output);

}

int main(int argc, char * argv[]) {

Debug(“This is test log:%d=%s”, 123, “ABCD”);

return 0;

}

运行结果为:

$ gcc main.c && ./a.out

This is test log:123=ABCD

我们可以看到有一个函数vsprintf,看它的声明:

int vsprintf(char *str, const char *format, va_list ap);

这个函数里面,我们使用了va_list作为变参的类型,至此我们得出结论,如果一个变参函数需要调用另一个变参函数,那么被调用的变参函数需要进行改造,使用va_list定参来模拟变参。

这样前面的Debug/Log函数需要改造成如下格式:

#include

#include

void Log(char * level, const char * format, va_list valist) {

char output[1024];

vsprintf(output, format, valist);

printf(“%s: %s\n”, level, output);

}

void Debug(const char * format, …) {

va_list valist;

va_start(valist, format);

Log(“DEBUG”, format, valist);

va_end(valist);

}

int main(int argc, char * argv[]) {

Debug(“This is test log:%d=%s”, 123, “ABCD”);

return 0;

}

2. Java语言的变参

2.1 变参函数声明

type func_name(type …params)

这个声明和C/C++的声明是一样的;不同点是

Java变参除了三个点(…)之外,还有名字和类型。

Java允许第一个参数就是变参。但同样不允许变参后面还有参数。

2.2 如何调用

调用方式和C/C++一样

public static void run(String… strings) {

for (String s: strings) {

System.out.println(“run: ” + s);

}

}

public static void main(String[] args) {

run(); // 变参个数为 0

run(“”); // 变参个数为 1

run(“AA”,”BB”); // 变参个数为 2

run(“AA”,”BB”, “CC”); // 变参个数为 3

}

运行结果

run:

run: AA

run: BB

run: AA

run: BB

run: CC

2.3 函数内如何分析变参

前面例子我们也看到了,很简单遍历参数就行了,不展开讲了。

2.4 一个变参函数如何调用另一变参函数

public static void run2(String … strings) {

for (String s: strings) {

System.out.println(“run2: ” + s);

}

}

public static void run1(String… strings) {

run2(strings);

}

public static void main(String[] args) {

run1(“AA”,”BB”, “CC”);

}

比起前面的C/C++,是不是感觉很幸福;太简单了,直接调用就行了。

到这里是不是可以完美的结束了呢,我问一个问题:

run1()和run2()都是变参函数,main传给run1()有三个参数(“AA”,”BB”,”CC”),run1()传给run2()的是参数strings,我的问题是run2()收到的是一个参数(String…类型),还是三个参数(String)类型。

改造一下代码:

public static void run2(String … strings) {

System.out.println(“run2 param type=” + strings.getClass().getName());

System.out.println(“run2 param size=” + strings.length);

}

public static void run1(String… strings) {

System.out.println(“run1 param type=” + strings.getClass().getName());

System.out.println(“run1 param size=” + strings.length);

run2(strings);

}

public static void main(String[] args) {

run1(“AA”,”BB”, “CC”);

}

再运行

run1 param type=[Ljava.lang.String;

run1 param size=3

run2 param type=[Ljava.lang.String;

run2 param size=3

我们会发现,不管在run1()里面还是在run2()里面收到的参数strings实际上是一个string数组类型,是不是java使用数组来包装变参呢?

答案是肯定的。调用者可以使用数组来传递变参。

public static void main(String[] args) {

String params[] = {“AA”,”BB”, “CC”};

run1(params);

// run1(“AA”,”BB”, “CC”);

}

上述两个调用方法是一样的效果。

再看一个例子:

public static void run2(Object … strings) {

System.out.println(“run2 param type=” + strings.getClass().getName());

System.out.println(“run2 param size=” + strings.length);

for (Object s: strings) {

System.out.println(“run2 param=” + s);

}

}

public static void run1(Object… strings) {

System.out.println(“run1 param type=” + strings.getClass().getName());

System.out.println(“run1 param size=” + strings.length);

run2(strings);

}

public static void main(String[] args) {

String params[] = {“AA”,”BB”, “CC”};

run1(params, “DD”);

}

运行结果:

run1 param type=[Ljava.lang.Object;

run1 param size=2

run2 param type=[Ljava.lang.Object;

run2 param size=2

run2 param=[Ljava.lang.String;@15db9742

run2 param=DD

理解一下为什么是这样。

因为run1()收到的变参实际上是一个数组,包含两个元素,第一个元素是一个字符串数组(“AA”,”BB”, “CC”),第二个元素是一个字符串(“DD”)。

3. go语言的变参

3.1 变参函数的声明

func func_name(args …interface{}) {

}

这种声明方式和java是一致的。

3.2 如何调用

func_name() # 变参个数为 0

func_name(“”) # 变参个数为 1

func_name(“AA”, “BB”, “CC”) # 变参个数为 3

3.3 参数分析

再看一个例子:

package main

import “fmt”

import “reflect”

func Log(args …string) {

fmt.Println(reflect.TypeOf(args))

fmt.Printf(“%d\n”, len(args))

}

func main() {

Log()

Log(“”)

Log(“INFO”, “AAA”, “This is a message”)

}

运行结果:

$ go build && ./main

[]string

0

[]string

1

[]string

3

这下发现golang的变参和java的变参原理是一样的,都是以数组的方式(golang用slice)组织参数,那么就可以简单的把变参理解到slice就非常清楚了。

3.4 一个变参函数如何调用另一变参函数

package main

import “fmt”

import “reflect”

func Debug(args …string) {

Log(“DEBUG”, args…)

}

func Log(level string, args …string) {

fmt.Println(reflect.TypeOf(args))

fmt.Printf(“%d\n”, len(args))

}

func main() {

Debug()

Debug(“”)

Debug(“AAA”, “This is a message”)

}

这个例子中main()=>Debug()=>Log(),Debug和Log都是变参函数;

注意在Debug里面调用Log的时候 需要显式的指定args是一个变参变量,这和java的使用方式有点差异。

正是这个差异,在golang里面变参和slice[]不是完全相等。虽然在java里面可以传一个数组给变参,但是在golang里面却不能传一个slice给变参。

package main

import “fmt”

import “reflect”

func Debug(args …string) {

ss := []string{“aa”, “bb”}

Log(“DEBUG”, ss)

}

func Log(level string, args …string) {

fmt.Println(reflect.TypeOf(args))

fmt.Printf(“%d\n”, len(args))

}

func main() {

Debug()

Debug(“”)

Debug(“AAA”, “This is a message”)

}

编译

./main.go:9: cannot use ss (type []string) as type string in argument to Log

可见在golang里面变参就是变参,不是slice对象;两者不能互通使用。

但是还是可以把slice标记为变参,通过在变量名后跟三个点(varname…)的格式,看下面例子代码:

package main

import “fmt”

import “reflect”

func Log(args …string) {

fmt.Println(reflect.TypeOf(args))

fmt.Printf(“%d\n”, len(args))

}

func main() {

ss1 := []string{}

ss2 := []string{“aa”, “bb”}

Log(ss1)

Log(ss2)

}

编译

$ go build && ./main

./main.go:15: cannot use ss1 (type []string) as type string in argument to Log

./main.go:16: cannot use ss2 (type []string) as type string in argument to Log

我们把他改成:

package main

import “fmt”

import “reflect”

func Log(args …string) {

fmt.Println(reflect.TypeOf(args))

fmt.Printf(“%d\n”, len(args))

}

func main() {

ss1 := []string{}

ss2 := []string{“aa”, “bb”}

//Log(ss1)

//Log(ss2)

Log(ss1…)

Log(ss2…)

}

再运行

$ go build && ./main

[]string

0

[]string

2

这样就运行的很好了。因为如果直接使用ss1那就是这是一个slice类型的参数,但是Log()函数并不支持slice类型参数,而如果换成ss1…表示这是多个字符串类型参数。

最后一个例子:

package main

import “fmt”

import “reflect”

func Log(args …interface{}) {

fmt.Println(reflect.TypeOf(args))

fmt.Printf(“%d\n”, len(args))

}

func main() {

ss1 := []interface{}{}

ss2 := []interface{}{“aa”, “bb”}

Log(ss1)

Log(ss2)

}

编译运行

$ go build && ./main

[]interface {}

1

[]interface {}

1

明白了把。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/15021.html

(0)
上一篇 2024-02-22 07:15
下一篇 2024-02-22 09:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信