1.概述
函数是非常重要的,我们写程序会把能复用的代码提取到一个函数中,方便下次复用,所以一个函数最好只做一件事情。在 golang 中函数是一等公民,可以定义为变量的类型、能够在函数参数中传递等。
2.声明
函数包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func name(parameter-list) (result-list) {
body
}
形式参数列表:参数名和参数类型。
返回值列表:参数名和参数类型,无返回值是可省略。可以定义多个返回值(这点和其他语言不同)
func add(x, y int) int {
return x + y
}
fmt.Println(add(3,4)) // 7
x 和 y 是行参名,3和4是调用时的传入的实参,函数返回一个 int 的值。返回值可以是命名的,命名的返回值是一个被初始化类型零值的局部变量。
func add(x, y int) (res int) { // 命名返回值,括号不能省略
res = x + y // res 是一个局部变量
return // 命名返回值时,直接使用 return 即可
}
fmt.Println(add(3,4)) // 7
形参和返回值有相同类型的,不需要为每一个形参和返回值定义类型。 "_" 代表忽略,未被使用的变量。
func f(i, j, k int, s, t string) (z,x int) {/* ... */}
func add(x int, y int) int {return x + y}
func sub(x, y int) (z int) { z = x - y; return}
func first(x int, _ int) int { return x }
func zero(int, int) int { return 0 }
fmt.Printf("%T\n", add) // "func(int, int) int"
fmt.Printf("%T\n", sub) // "func(int, int) int"
fmt.Printf("%T\n", first) // "func(int, int) int"
fmt.Printf("%T\n", zero) // "func(int, int) int"
实参:调用者传递给参数的值。实参是值传递,所以形参是实参的拷贝。对形参进行修改不会影响实参。但是,如果实参包括引用类型,如指针,slice、map、function、channel等类型,实参可能会由于函数的间接引用被修改。
3.可变参数
参数数量可变的被称为可变参数。使用 "..." 表示,同时可变参数只能被放在形参列表最后。
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
sum 函数返回任意个 int 参数的和。在函数体中,vals被看作是类型为[] int的切片。sum 可以接收任意数量的int型参数:
fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"
在上面的代码中,调用者隐式的创建一个数组,并将原始参数复制到数组中,再把数组的一个切片作为参数传给被调用函数。如果原始参数已经是切片类型,参数后使用"..."即可:
values := []int{1, 2, 3, 4}
fmt.Println(sum(values...)) // "10"
4.匿名函数与闭包
package main
import "fmt"
func main() {
f := closure(10)
fmt.Println(f(1))
fmt.Println(f(2))
}
func closure(x int) func(int) int {
fmt.Printf("%p\n", &x)
return func(y int) int {
fmt.Printf("%p\n", &x)
return x + y
}
}
返回:
0xc420076008
0xc420076008
11
0xc420076008
12
变量 f 的值是一个函数,里面保存着对变量 x 的引用,形成了闭包。可以想象成 f 中存了 x 的地址。
6.defer 用法
go 的 defer 会在当前函数返回前调用其后的函数。一般被用来做资源释放关闭、 panic recover等
package main
import (
"fmt"
"os"
)
var sum int
func main() {
open, err := os.Open("file.txt")
// 先判断错误,打开失败就不需要 close
if err != nil {
fmt.Println(err)
return
}
defer open.Close()
}
defer 会延迟调用后面的函数:
func main() {
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
}
// outut:
// 2
// 1
// 0
7.panic 与 recover
package main
import "fmt"
func main() {
A()
B()
C()
}
func A() {
fmt.Println("func A")
}
func B() {
// 此处defer的匿名函数要注册在panic之前,否则程序还未执行到该函数就终止了
defer func() {
if err := recover(); err != nil {
fmt.Println("recover in B")
}
}()
panic("panic in B")
}
func C() {
fmt.Println("func C")
}