函数
2026年9月6日
函数把一段能复用的逻辑包起来:接收参数,算出返回值。main 也是函数。
函数定义
func add(a int, b int) int {
return a + b
}
go
func、名字、参数列表、返回类型、函数体。没有返回值就省略最后那个类型。
函数参数
相邻参数类型相同,可以只写一次:
func add(a, b int) int {
return a + b
}
go
个数不定用 ...T,函数里它是 []T:
func sum(nums ...int) int {
s := 0
for _, n := range nums {
s += n
}
return s
}
sum(1, 2, 3)
sum(append([]int{1}, 2, 3)...)
go
函数返回值
可以返回多个值。错误通常放在最后一个。
func div(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("除数是 0")
}
return a / b, nil
}
go
给返回值起名,函数里可以直接 return:
func split(s string) (head, tail string) {
if len(s) == 0 {
return
}
head, tail = s[:1], s[1:]
return
}
go
匿名函数
没有名字,可以立刻调用,也可以赋给变量:
func(msg string) {
fmt.Println(msg)
}("hello")
greet := func(name string) string {
return "hi, " + name
}
fmt.Println(greet("槿"))
go
高阶函数
函数本身也是值,可以当参数、当返回值。按输入选不同操作时,把操作提成函数传进去:
func apply(a, b int, op func(int, int) int) int {
return op(a, b)
}
fmt.Println(apply(3, 4, add))
fmt.Println(apply(3, 4, func(x, y int) int { return x * y }))
go
闭包
内层函数能用到外层的变量。例如先传入一个延时,返回的函数再接收数字去做求和:
func delayedSum(delay time.Duration) func(...int) int {
return func(nums ...int) int {
time.Sleep(delay)
s := 0
for _, n := range nums {
s += n
}
return s
}
}
sum := delayedSum(100 * time.Millisecond)
fmt.Println(sum(1, 2, 3))
go
闭包里改外层变量,改的是那一块,不是拷贝。循环里开协程时尤其容易踩这个点。
值传递和引用传递
变量在内存里占一块地。函数参数默认拷贝一份再传进去。函数里改这份拷贝,外面那份不动。
func inc(n int) {
n++
}
x := 1
inc(x)
fmt.Println(x) // 还是 1
go
要在函数里改外面的值,把地址传进去:
func inc(n *int) {
*n++
}
x := 1
inc(&x)
fmt.Println(x) // 2
go
切片、map、channel 作参数时,拷贝的是「描述信息」,底层数据还是同一份,所以函数里改元素外面看得到。但 append 换了底层数组,外面那根切片不一定跟着变,需要的话把新切片 return 回去。
指针
&x:取x的地址,类型是*T*p:顺着地址找到值(解引用)
x := 10
p := &x
fmt.Println(*p) // 10
*p = 20
fmt.Println(x) // 20
go
new(T) 会分配一个 T 的零值,返回指针。指针的零值是 nil,对 nil 解引用会 panic。