c语言 可变参数字符串函数,Go语言可变参数(变参函数)

在C语言时代大家一般都用过printf()函数,从那个时候开始其实已经在感受可变参数的魅力和价值。如同C语言中的printf()函数,Go语言标准库中的fmt.Println()等函数的实现也严重依赖于语言的可变参数功能。

本节我们将介绍可变参数的用法。合适地使用可变参数,可以让代码简单易用,尤其是输入输出类函数,比如日志函数等。

可变参数类型

可变参数是指函数传入的参数个数为不定数量。为了做到这点,首先需要将函数定义为接受可变参数类型:

funcmyfunc(args...int){for_,arg:=rangeargs{fmt.Println(arg)}}

上面这段代码的意思是,函数myfunc()接受不定数量的参数,这些参数的类型全部是int,所以它可以用如下方式调用:

myfunc(2,3,4)

myfunc(1,3,7,13)

形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。它是一个语法糖(syntacticsugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的机会。

从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为什么上面的参数args可以用for循环来获得每个传入的参数。

假如没有...type这样的语法糖,开发者将不得不这么写:

funcmyfunc2(args[]int){for_,arg:=rangeargs{fmt.Println(arg)}}

从函数的实现角度来看,这没有任何影响,该怎么写就怎么写。但从调用方来说,情形则完全不同:

myfunc2([]int{1,3,7,13})

会发现,我们不得不加上[]int{}来构造一个数组切片实例。但是有了…type这个语法糖,我们就不用自己来处理了。

可变参数的传递

假设有另一个变参函数叫做myfunc3(args…int),下面的例子演示了如何向其传递变参:

funcmyfunc(args...int){//按原样传递myfunc3(args...)//传递片段,实际上任意的intslice都可以传进去myfunc3(args[1:]...)}

任意类型的可变参数

之前的例子中将可变参数类型约束为int,如果你希望传任意类型,可以指定类型为interface{}。下面是Go语言标准库中fmt.Printf()的函数原型:

funcPrintf(formatstring,args…interface{}){

//…

}

用interface{}传递任意类型数据是Go语言的惯例用法。使用interface{}仍然是类型安全的,这和C/C++不太一样。下面通过示例来了解一下如何分派传入interface{}类型的数据。

packagemainimport"fmt"funcMyPrintf(args...interface{}){for_,arg:=rangeargs{switcharg.(type){caseint:fmt.Println(arg,"isanintvalue.")casestring:fmt.Println(arg,"isastringvalue.")caseint64:fmt.Println(arg,"isanint64value.")default:fmt.Println(arg,"isanunknowntype.")}}}funcmain(){varv1int=1varv2int64=234varv3string="hello"varv4float32=1.234MyPrintf(v1,v2,v3,v4)}

该程序的输出结果为:

1isanintvalue.

234isanint64value.

helloisastringvalue.

1.234isanunknowntype.

遍历可变参数列表——获取每一个参数的值

可变参数列表的数量不固定,传入的参数是一个切片。如果需要获得每一个参数的具体值时,可以对可变参数变量进行遍历,参见下面代码:

packagemainimport("bytes""fmt")//定义一个函数,参数数量为0~n,类型约束为字符串funcjoinStrings(slist...string)string{//定义一个字节缓冲,快速地连接字符串varbbytes.Buffer//遍历可变参数列表slist,类型为[]stringfor_,s:=rangeslist{//将遍历出的字符串连续写入字节数组b.WriteString(s)}//将连接好的字节数组转换为字符串并输出returnb.String()}funcmain(){//输入3个字符串,将它们连成一个字符串fmt.Println(joinStrings("pig","and","rat"))fmt.Println(joinStrings("hammer","mom","and","hawk"))}

代码输出如下:

pigandrat

hammermomandhawk

代码说明如下:

第8行,定义了一个可变参数的函数,slist的类型为[]string,每一个参数的类型是string,也就是说,该函数只接受字符串类型作为参数。

第11行,bytes.Buffer在这个例子中的作用类似于StringBuilder,可以高效地进行字符串连接操作。

第13行,遍历slist可变参数,s为每个参数的值,类型为string。

第15行,将每一个传入参数放到bytes.Buffer中。

第19行,将bytes.Buffer中的数据转换为字符串作为函数返回值返回。

第24行,输入3个字符串,使用joinStrings()函数将参数连接为字符串输出。

第25行,输入4个字符串,连接后输出。

如果要获取可变参数的数量,可以使用len()函数对可变参数变量对应的切片进行求长度操作,以获得可变参数数量。

获得可变参数类型——获得每一个参数的类型

当可变参数为interface{}类型时,可以传入任何类型的值。此时,如果需要获得变量的类型,可以通过switch类型分支获得变量的类型。下面的代码演示将一系列不同类型的值传入printTypeValue()函数,该函数将分别为不同的参数打印它们的值和类型的详细描述。

打印类型及值:

packagemainimport("bytes""fmt")funcprintTypeValue(slist...interface{})string{//字节缓冲作为快速字符串连接varbbytes.Buffer//遍历参数for_,s:=rangeslist{//将interface{}类型格式化为字符串str:=fmt.Sprintf("%v",s)//类型的字符串描述vartypeStringstring//对s进行类型断言switchs.(type){casebool://当s为布尔类型时typeString="bool"casestring://当s为字符串类型时typeString="string"caseint://当s为整型类型时typeString="int"}//写值字符串前缀b.WriteString("value:")//写入值b.WriteString(str)//写类型前缀b.WriteString("type:")//写类型字符串b.WriteString(typeString)//写入换行符b.WriteString("n")}returnb.String()}funcmain(){//将不同类型的变量通过printTypeValue()打印出来fmt.Println(printTypeValue(100,"str",true))}

代码输出如下:

value:100type:int

value:strtype:string

value:truetype:bool

代码说明如下:

第8行,printTypeValue()输入不同类型的值并输出类型和值描述。

第11行,bytes.Buffer字节缓冲作为快速字符串连接。

第14行,遍历slist的每一个元素,类型为interface{}。

第17行,使用fmt.Sprintf配合%v动词,可以将interface{}格式的任意值转为字符串。

第20行,声明一个字符串,作为变量的类型名。

第23行,switchs.(type)可以对interface{}类型进行类型断言,也就是判断变量的实际类型。

第24~29行为s变量可能的类型,将每种类型的对应类型字符串赋值到typeString中。

第33~42行为写输出格式的过程。

在多个可变参数函数中传递参数

可变参数变量是一个包含所有参数的切片,如果要在多个可变参数中传递参数,可以在传递时在可变参数变量中默认添加...,将切片中的元素进行传递,而不是传递可变参数变量本身。

下面的例子模拟print()函数及实际调用的rawPrint()函数,两个函数都拥有可变参数,需要将参数从print传递到rawPrint中。

可变参数传递:

packagemainimport"fmt"//实际打印的函数funcrawPrint(rawList...interface{}){//遍历可变参数切片for_,a:=rangerawList{//打印参数fmt.Println(a)}}//打印函数封装funcprint(slist...interface{}){//将slist可变参数切片完整传递给下一个函数rawPrint(slist...)}funcmain(){print(1,2,3)}

代码输出如下:

1

2

3

对代码的说明:

第9~13行,遍历rawPrint()的参数列表rawList并打印。

第20行,将变量在print的可变参数列表中添加...后传递给rawPrint()。

第25行,传入1、2、3这3个整型值进行打印。

如果尝试将第20行修改为:

rawPrint("fmt",slist)

再次执行代码,将输出:

[123]

此时,slist(类型为[]interface{})将被作为一个整体传入rawPrint(),rawPrint()函数中遍历的变量也就是slist的切片值。

可变参数使用...进行传递与切片间使用append连接是同一个特性。

你可能感兴趣的:(c语言,可变参数字符串函数)