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
    }
}
  1. 函数命名:使用驼峰命名法,导出函数首字母大写
  2. 单一职责:每个函数只做一件事
  3. 参数数量:尽量避免超过5个参数
  4. 错误处理:多返回error类型进行错误处理
  5. 避免副作用:尽量保持函数的纯粹性
  6. 适当注释:为导出的函数添加注释说明
返回列表