共计 3082 个字符,预计需要花费 8 分钟才能阅读完成。
Go 语言入门系列后面的文章:
- Go 语言入门系列 (三) 之数组和切片
- Go 语言入门系列 (四) 之 map 的应用
- Go 语言入门系列 (五) 之指针和构造体的应用
在 Go 语言入门系列 (二) 之根底语法总结这篇文章中曾经介绍过了 Go 语言的函数的根本应用,包含申明、参数、返回值。本文再具体介绍一下函数的其余应用。
1. 变参
Go 语言的函数除了反对 0 个或多个参数,还反对不定数量的参数,即变参。申明形式为:
func foo(变参名 ... 参数类型) 函数类型 {// 函数体}
上面是一个具体的函数,它接管不定数量的 int
参数,并返回和:
package main
import "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 main
import "fmt"
// 传入一个值,打印它
func printX(x int) {fmt.Println(x)
}
func main() {
var a int = 5
printX(a) // 向函数中传入参数:变量 a
}
这里有一个问题:咱们真的是把变量 a
传给了 printX
函数吗?咱们用两个例子来阐明问题。
2.1. 例 1
package main
import "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 main
import "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 main
import "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 main
import "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 main
import "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 语言入门系列」。
如有谬误,还请斧正。