Golang的函数
函数是Go语言中的基本构建块,用于组织可重用的代码逻辑。本文将详细介绍Go中函数的定义、参数传递、返回值以及高级用法。
1. 函数的基本格式
完整语法结构
func 函数名(参数列表) (返回值列表) {
// 函数体
return 返回值
}
组成部分说明
| 组成部分 | 说明 | 是否必需 |
|---|---|---|
func |
函数关键字 | 必需 |
| 函数名 | 标识函数名称 | 必需 |
| 参数列表 | (参数名 参数类型, ...) |
可选 |
| 返回值列表 | (返回值名 返回值类型, ...) |
可选 |
| 函数体 | { ... } |
必需 |
return |
返回语句 | 根据返回值类型决定 |
2. 函数的参数传递
Go语言中的参数传递分为两种方式:值传递和引用传递。
值传递
值传递会将实参的值复制一份传递给函数,函数内部对参数的修改不会影响外部变量。
示例代码
package main
import "fmt"
func main() {
x := 12
fmt.Printf("进入函数前,函数外部x的值为:%d\n", x)
add(x)
fmt.Printf("进入函数后,函数外部x的值还是:%d\n", x)
}
func add(x int) int {
x += 12
fmt.Printf("函数内部,x完成加12,值为:%d\n", x)
return x
}
输出:
进入函数前,函数外部x的值为:12
函数内部,x完成加12,值为:24
进入函数后,函数外部x的值还是:12
原理分析:
外部变量 x = 12
|
| 值传递:复制一份 12
↓
函数参数 x = 12 (独立副本)
|
| 函数内部修改
↓
函数内部 x = 24
|
| 函数结束后副本销毁
↓
外部变量 x = 12 (不受影响)
引用传递
引用传递通过传递变量的内存地址(指针),函数内部通过指针修改数据会直接影响外部变量。
示例代码
package main
import "fmt"
func main() {
x := 12
fmt.Printf("进入函数前,函数外部x的值为:%d\n", x)
add2(&x) // 传递x的内存地址
fmt.Printf("进入函数后,函数外部x的值变为:%d\n", x)
}
func add2(x *int) int {
*x += 12 // 通过指针修改值
fmt.Printf("函数内部,x完成加12,值为:%d\n", *x)
return *x
}
输出:
进入函数前,函数外部x的值为:12
函数内部,x完成加12,值为:24
进入函数后,函数外部x的值变为:24
原理分析:
外部变量 x = 12 (内存地址: 0x1000)
|
| 引用传递:传递地址 0x1000
↓
函数参数 x = 0x1000 (指针)
|
| 通过地址修改内存
↓
*操作符解引用,修改地址0x1000的值
|
↓
外部变量 x = 24 (同一块内存被修改)
值传递 vs 引用传递对比
| 对比维度 | 值传递 | 引用传递(指针) |
|---|---|---|
| 传递内容 | 数据的副本 | 数据的内存地址 |
| 内存占用 | 复制整个数据 | 只复制指针(8字节) |
| 修改影响 | 不影响外部变量 | 影响外部变量 |
| 大对象性能 | 开销较大 | 开销较小 |
| 安全性 | 高(不会意外修改外部数据) | 需要谨慎使用 |
| 语法标识 | 直接传值 | 使用&取地址和*解引用 |
3. 函数的参数类型
多个参数
func add(a int, b int) int {
return a + b
}
// 连续参数类型可以简写
func add(a, b int) int {
return a + b
}
可变参数
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// 调用方式
result := sum(1, 2, 3, 4, 5) // result = 15
4. 函数的返回值
单返回值
func add(a int, b int) int {
return a + b
}
多返回值
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为零")
}
return a / b, nil
}
命名返回值
func getCoordinates() (x, y int) {
x = 10
y = 20
return // 裸返回,自动返回x和y
}
5. 匿名函数和闭包
匿名函数
// 定义并立即执行
func main() {
result := func(a, b int) int {
return a + b
}(10, 20)
fmt.Println(result) // 30
}
闭包(Closure)
func getCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
counter := getCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
}
6. 函数作为类型
定义函数类型
type Operation func(int, int) int
func add(a, b int) int {
return a + b
}
func subtract(a, b int) int {
return a - b
}
func main() {
var op Operation
op = add
fmt.Println(op(10, 5)) // 15
op = subtract
fmt.Println(op(10, 5)) // 5
}
函数作为参数
func calculate(a, b int, op func(int, int) int) int {
return op(a, b)
}
func main() {
result := calculate(10, 5, func(a, b int) int {
return a * b
})
fmt.Println(result) // 50
}
7. defer延迟执行
defer语句用于在函数返回前执行某些操作,常用于资源清理。
func readFile() {
file, err := os.Open("test.txt")
if err != nil {
return
}
defer file.Close() // 函数返回前自动关闭文件
// 读取文件内容...
fmt.Println("读取文件成功")
}
defer的特性
- 多个defer按后进先出(LIFO)顺序执行
- defer在函数返回之前执行
- defer的参数在定义时就已经确定
func deferExample() {
defer fmt.Println("第三")
defer fmt.Println("第二")
defer fmt.Println("第一")
fmt.Println("开始执行")
}
// 输出:
// 开始执行
// 第一
// 第二
// 第三
8. init函数
init函数在包初始化时自动执行,用于初始化包级别的变量。
package main
import "fmt"
func init() {
fmt.Println("init函数执行")
}
func main() {
fmt.Println("main函数执行")
}
// 输出:
// init函数执行
// main函数执行
9. 完整示例程序
package main
import "fmt"
func main() {
// 值传递示例
fmt.Println("=== 值传递 ===")
x := 12
fmt.Printf("进入函数前,函数外部x的值为:%d\n", x)
add(x)
fmt.Printf("进入函数后,函数外部x的值还是:%d\n\n", x)
// 引用传递示例
fmt.Println("=== 引用传递 ===")
y := 12
fmt.Printf("进入函数前,函数外部y的值为:%d\n", y)
add2(&y)
fmt.Printf("进入函数后,函数外部y的值变为:%d\n\n", y)
// 多返回值示例
fmt.Println("=== 多返回值 ===")
quotient, remainder := divide(17, 5)
fmt.Printf("17 / 5 = %d 余 %d\n\n", quotient, remainder)
// 闭包示例
fmt.Println("=== 闭包示例 ===")
counter := getCounter()
fmt.Println(counter())
fmt.Println(counter())
fmt.Println(counter())
}
// 值传递函数
func add(x int) int {
x += 12
fmt.Printf("函数内部,x完成加12,值为:%d\n", x)
return x
}
// 引用传递函数(指针)
func add2(x *int) int {
*x += 12
fmt.Printf("函数内部,x完成加12,值为:%d\n", *x)
return *x
}
// 多返回值函数
func divide(a, b int) (int, int) {
return a / b, a % b
}
// 闭包函数
func getCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
- 函数命名:使用驼峰命名法,导出函数首字母大写
- 单一职责:每个函数只做一件事
- 参数数量:尽量避免超过5个参数
- 错误处理:多返回error类型进行错误处理
- 避免副作用:尽量保持函数的纯粹性
- 适当注释:为导出的函数添加注释说明