Go 函数
本章将介绍 Go 语言中的函数,包括函数定义、参数、返回值和作用域。
函数基础
定义函数
func 函数名(参数) 返回值 {
// 函数体
return 返回值
}
基本示例
// 无参数无返回值
func sayHello() {
fmt.Println("Hello!")
}
// 有参数无返回值
func greet(name string) {
fmt.Println("Hello,", name)
}
// 有参数有返回值
func add(a int, b int) int {
return a + b
}
// 调用
sayHello()
greet("张三")
result := add(1, 2)
fmt.Println(result) // 3
参数
多参数
// 相同类型可以合并
func add(a, b int) int {
return a + b
}
// 不同类型分开写
func greet(name string, age int) {
fmt.Printf("Hello, %s, age: %d\n", name, age)
}
不定参数
不定参数允许传入零个或多个参数:
// 语法:...类型
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// 调用
fmt.Println(sum()) // 0
fmt.Println(sum(1)) // 1
fmt.Println(sum(1, 2)) // 3
fmt.Println(sum(1, 2, 3)) // 6
切片展开
可以使用切片调用不定参数函数:
nums := []int{1, 2, 3, 4, 5}
result := sum(nums...) // 展开切片
fmt.Println(result) // 15
参数是值拷贝
Go 中函数参数是值拷贝,不会修改原变量:
func modify(x int) {
x = 100
}
func main() {
a := 10
modify(a)
fmt.Println(a) // 10,原值不变
}
使用指针修改参数
func modify(x *int) {
*x = 100
}
func main() {
a := 10
modify(&a)
fmt.Println(a) // 100,原值被修改
}
返回值
单返回值
func add(a, b int) int {
return a + b
}
多返回值
Go 函数可以返回多个值:
func divide(a, b int) (int, int) {
quotient := a / b
remainder := a % b
return quotient, remainder
}
// 调用
q, r := divide(10, 3)
fmt.Println(q, r) // 3 1
// 忽略某个返回值
q, _ := divide(10, 3)
命名返回值
可以为返回值命名,函数结束时自动返回:
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 自动返回 x 和 y
}
// 调用
a, b := split(100)
fmt.Println(a, b) // 44 56
小技巧
命名返回值使代码更清晰,但过度使用会使代码难以理解。建议仅在多个返回值的场景使用。
返回错误
Go 的惯用方式是通过返回 error 来处理错误:
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// 调用
result, err := divide(10, 0)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}
函数类型
函数作为参数
Go 中函数是一等公民,可以作为参数传递:
func apply(fn func(int, int) int, a, b int) int {
return fn(a, b)
}
func main() {
result := apply(func(a, b int) int {
return a + b
}, 10, 20)
fmt.Println(result) // 30
}
函数作为返回值
func multiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
func main() {
double := multiplier(2)
triple := multiplier(3)
fmt.Println(double(5)) // 10
fmt.Println(triple(5)) // 15
}
函数作为值
type Operation func(int, int) int
func add(a, b int) int {
return a + b
}
func main() {
var op Operation = add
fmt.Println(op(10, 20)) // 30
op = func(a, b int) int {
return a * b
}
fmt.Println(op(10, 20)) // 200
}
闭包
闭包是指一个函数可以访问并操作其外部作用域的变量:
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
c1 := counter()
c2 := counter()
fmt.Println(c1()) // 1
fmt.Println(c1()) // 2
fmt.Println(c1()) // 3
fmt.Println(c2()) // 1
fmt.Println(c2()) // 2
}
闭包修改变量
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
addJpg := makeSuffix(".jpg")
addTxt := makeSuffix(".txt")
fmt.Println(addJpg("photo")) // photo.jpg
fmt.Println(addJpg("photo.jpg")) // photo.jpg
fmt.Println(addTxt("doc")) // doc.txt
}
defer
defer 用于延迟执行函数调用,常用于资源清理:
基本用法
func read() {
file, _ := os.Open("test.txt")
defer file.Close() // 函数结束时自动关闭
// 读取文件...
}
多个 defer
多个 defer 按后进先出(LIFO)顺序执行:
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("主函数")
}
// 输出:
// 主函数
// 3
// 2
// 1
defer 与返回值
func f1() int {
i := 0
defer func() {
i++
}()
return i // 返回 0
}
func f2() (i int) {
defer func() {
i++
}()
return i // 返回 1
}
defer 常见用途
// 1. 关闭文件
func readFile(path string) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
// 读取文件...
}
// 2. 解锁互斥锁
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
defer mu.Unlock()
counter++
}
// 3. 打印函数执行时间
func timeTrack(start time.Time, name string) {
elapsed := time.Since(start)
fmt.Printf("%s 执行耗时: %s\n", name, elapsed)
}
func slowFunction() {
defer timeTrack(time.Now(), "slowFunction")
time.Sleep(2 * time.Second)
}
恐慌和恢复
panic
panic 会导致程序崩溃,通常用于不可恢复的错误:
func panicExample() {
panic("程序崩溃")
fmt.Println("这行不会执行")
}
recover
recover 用于捕获 panic,防止程序崩溃:
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获到 panic:", r)
}
}()
panic("发生错误")
fmt.Println("这行不会执行")
}
最佳实践
func safeFunction(fn func()) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
fn()
return nil
}
递归
函数可以调用自身:
// 阶乘
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
// 斐波那契数列
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// 尾递归优化(Go 不支持尾递归优化)
func factorialTail(n, acc int) int {
if n <= 1 {
return acc
}
return factorialTail(n-1, n*acc)
}
递归终止条件
// 错误示例:没有终止条件
func countDown(n int) {
fmt.Println(n)
countDown(n - 1) // 无限递归
}
// 正确示例
func countDown(n int) {
if n < 0 {
return // 终止条件
}
fmt.Println(n)
countDown(n - 1)
}
main 函数
main 函数是程序入口:
package main
import "fmt"
func main() {
fmt.Println("程序开始")
}
注意
- main 函数必须在 main 包中
- 每个程序只能有一个 main 函数
- main 函数不接受任何参数,也不返回任何值
init 函数
init 函数在包被导入时执行:
package main
import "fmt"
var name string
func init() {
name = "张三"
fmt.Println("init 函数执行")
}
func main() {
fmt.Println("main 函数执行:", name)
}
// 输出:
// init 函数执行
// main 函数执行: 张三
提示
init 函数常用于包级别的初始化,如设置配置、初始化全局变量等。
小结
- 函数定义:使用 func 关键字
- 参数:值传递,使用指针修改原值
- 返回值:支持多返回值,常用 error 处理
- 函数类型:函数可以作为参数和返回值
- 闭包:函数可以访问外部变量
- defer:延迟执行,常用于资源清理
- panic/recover:错误处理机制
- 递归:函数调用自身
练习
- 编写一个函数,计算两个数的和、差、积
- 编写一个函数,接受不定参数并计算平均值
- 使用闭包实现一个计数器
- 编写一个函数,使用 defer 在函数返回前打印执行时间
- 使用递归实现一个函数来计算斐波那契数列第 n 项
- 编写一个安全的除法函数,返回错误信息