Go语言入门系列后面的文章:
- Go语言入门系列(三)之数组和切片
- Go语言入门系列(四)之map的应用
- Go语言入门系列(五)之指针和构造体的应用
在Go语言入门系列(二)之根底语法总结这篇文章中曾经介绍过了Go语言的函数的根本应用,包含申明、参数、返回值。本文再具体介绍一下函数的其余应用。
1. 变参
Go语言的函数除了反对0个或多个参数,还反对不定数量的参数,即变参。申明形式为:
func foo(变参名 ...参数类型) 函数类型 { //函数体}
上面是一个具体的函数,它接管不定数量的int
参数,并返回和:
package mainimport "fmt"func add(arg ...int) int { //变参函数 var sum int for _, value := range arg { sum += value } return sum}func main() { sum := add(1, 2, 3, 4) fmt.Println(sum) //10}
arg ...int
表明add
函数接管不定数量的参数,且只能是int
类型的。arg
是咱们给该变参取的名字,它实际上是一个切片,所以在add
函数中能够应用range
遍历变量arg
。
2. 传值和传指针
当咱们调用一个有参函数时,必定会向该函数中传入参数:
package mainimport "fmt"//传入一个值,打印它func printX(x int) { fmt.Println(x)}func main() { var a int = 5 printX(a) //向函数中传入参数:变量a}
这里有一个问题:咱们真的是把变量a
传给了printX
函数吗?咱们用两个例子来阐明问题。
2.1. 例1
package mainimport "fmt"func plusOne(x int) int { x = x + 1 fmt.Println("执行加一") return x}func main() { a := 5 fmt.Println("a =", a) //应该为5 理论为5 b := plusOne(a) fmt.Println("a =", a) //应该为6 理论为5 fmt.Println("b =", b) //应该为6 理论为6}
plusOne
函数的作用是把传进来的参数加一,并返回后果。
a=5
传进plusOne
函数,执行了x = x + 1
语句,那么执行过后a
的值应该为6,但理论为5。
变量b
接管了函数的返回值,所以为6,这没问题。
这证实了,咱们的a
变量基本就没传进函数中,那么理论传的是什么?理论传的是a
变量的一份拷贝。
所以,咱们向Go语言中的函数传入一个值,实际上传的是该值的拷贝,而非该值自身。
那如果咱们的确要把上例中的变量a
传入plusOne
函数中呢?那此时就不应该传值了,而是应该传入指针。代码改良如下:
package mainimport "fmt"func plusOne(x *int) int { //参数是指针变量 *x = *x + 1 fmt.Println("执行加一") return *x}func main() { a := 5 fmt.Println("a =", a) //应该为5 理论为5 b := plusOne(&a) //传入地址 fmt.Println("a =", a) //应该为6 理论为6 fmt.Println("b =", b) //应该为6 理论为6}
a=5
传进plusOne
函数,执行了x = x + 1
语句,执行过后a
的值理论为6。
这就证实,变量a
的确被传进plusOne
函数并被批改了。因为咱们传进去的是一个指针,即变量的地址,有了地址咱们能够间接操作变量。
如果你对指针的应用不相熟,这里的代码可能会有点难了解,上面逐行解释:
func plusOne(x *int) int {
申明x
是一个int
类型的指针参数,只承受int
类型变量的地址 。
*x = *x + 1
应用*
操作符依据x
中存的地址,获取到对应的值,而后加一。
return *x
应用*
操作符依据x
中存的地址,获取到对应的值,而后返回。
b := plusOne(&a)
plusOne
函数只承受int
类型变量的地址,所以应用&
操作符获取a
变量的地址,而后才传入。
2.2. 例2
上面我再举一个经典的例子:写一个函数,可能替换两个变量的值。
如果你不晓得什么是传值和传指针,那可能会写成这样:
package mainimport "fmt"func swap(x, y int) { tmp := x x = y y = tmp fmt.Println("函数中:x =", x, ", y =", y)}func main() { x, y := 2, 8 fmt.Println("替换前:x =", x, ", y =", y) swap(x, y) fmt.Println("替换后:x =", x, ", y =", y)}
运行后果:
替换前:x = 2 , y = 8函数中:x = 8 , y = 2替换后:x = 2 , y = 8
只在函数中实现了替换,出了函数又变回原样了。
想要实现替换,就必须传入指针,而非值拷贝:
package mainimport "fmt"func swap(x, y *int) { tmp := *x *x = *y *y = tmp fmt.Println("函数中:x =", *x, ", y =", *y)}func main() { x, y := 2, 8 fmt.Println("替换前:x =", x, ", y =", y) swap(&x, &y) fmt.Println("替换后:x =", x, ", y =", y)}
运行后果:
替换前:x = 2 , y = 8函数中:x = 8 , y = 2替换后:x = 8 , y = 2
传入指针可能真正替换两个变量的值。
传入指针的益处:
- 传入指针使咱们可能在函数中间接操作变量,多个函数也能操作同一个变量。
- 不须要再拷贝一遍值了。如果你须要传入比拟大的构造体,再拷贝一遍就多破费零碎开销了,而传入指针则小的多。
3. 函数作为值
在Go语言中,函数也能够作为值来传递。上面是一个例子:
package mainimport "fmt"type calculate func(int, int) int // 申明了一个函数类型func sum(x, y int) int { return x + y}func product(x, y int) int { return x * y}func choose(a, b int, f calculate) int { //函数作为参数 return f(a, b)}func main(){ diff := func(x, y int) int { //函数作为值赋给diff return x - y } fmt.Println(choose(2, 3, sum)) //5 fmt.Println(choose(4, 5, product)) //20 fmt.Println(choose(6, 7, diff)) //-1 fmt.Println(diff(9, 8)) //1}
函数作为值或者参数必定要有对应的类型,类型是:func(参数类型)返回值类型
。比方func(int,int) int
能够应用type
关键字给func(int,int) int
起个别名叫calculate
,方便使用。
choose
函数中申明了一个类型为calculate
的函数参数f
,而咱们又编写了calculate
类型的函数sum
和product
,所以能够向choose
函数中传入这两个函数。
咱们给变量diff
赋了一个函数,所以可能应用diff(9, 8)
,或者将其作为参数传入choose
函数。
作者简介
我是「行小观」,于千万人中的一个普通人。阴差阳错地走上了编程这条路,既然走上了这条路,那么我会尽可能远地走上来。我会在公众号『行人观学』中继续更新「Java」、「Go」、「数据结构和算法」、「计算机根底」等相干文章。
欢送关注,咱们一起踏上行程。
本文章属于系列文章「Go语言入门系列」。
如有谬误,还请斧正。