函数

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。


参考文档