Skip to content

Go 函数

函数是 Go(以及大多数编程语言)中的基本构建块。它们是用于执行特定任务的可重用代码块。每个 Go 程序至少包含一个函数:main(),它作为程序的入口点。

将代码分解为更小、定义明确的函数可以提高组织性、可读性和可维护性。每个函数理想情况下应具有单一、明确的目的。

函数声明(declaration)介绍其名称、参数和返回值类型。函数定义(definition)提供函数调用时实际执行的代码(函数体)。

Go 的标准库提供了许多有用的内置函数。例如,len() 可以确定字符串、数组、切片、map 和 channel 的长度。

Go 中定义函数的语法如下:

func functionName(parameterList) returnTypeList {
// Body of the function: statements to execute
// ...
return // Optional: returns values if specified in returnTypeList
}

函数定义的组成部分:

  • func 关键字: 标记函数定义的开始。
  • functionName: 您为函数选择的名称。遵循 Go 命名约定(未导出使用 camelCase,导出使用 PascalCase)。
  • parameterList: 括号 () 内的参数列表,用逗号 , 分隔。每个参数有一个名称后跟其类型(例如 name string, age int)。如果连续参数共享相同的类型,可以省略除最后一个之外所有参数的类型(例如 x, y int)。函数可以没有参数 ()。
  • returnTypeList: 指定函数返回的值的类型。可以是一个类型(int),多个类型用括号括起来((string, error)),或者如果函数不返回任何值则完全省略。
  • 函数体: 花括号 {} 包含的代码块,包含执行函数任务的语句。
  • return 语句: 用于从函数返回值的语句。返回值的类型必须与 returnTypeList 匹配。如果函数没有返回值类型,return 可以省略或单独使用以提前退出。

这是一个 max 函数,它接受两个整数并返回较大的那个:

// 函数定义
// 接受两个 'int' 类型参数 (num1, num2)
// 返回一个 'int' 类型值
func max(num1, num2 int) int {
var result int // 声明一个局部变量
if num1 > num2 {
result = num1
} else {
result = num2
}
return result // 返回计算出的最大值
}

要使用函数,需要通过其名称调用它,后跟括号 (),括号中包含与函数参数对应的实参(实际值)。

调用函数时,程序控制权会转移到该函数。函数执行其函数体,当遇到 return 语句或右花括号 } 时,控制权返回到函数被调用的地方。如果函数返回了值,可以将这些值赋给变量。

package main
import "fmt"
// 函数定义(同上)
func max(num1, num2 int) int {
if num1 > num2 {
return num1
}
return num2 // 简化的返回
}
func main() {
// 在 main 中定义局部变量
a := 100
b := 200
var maxValue int
// 使用 'a' 和 'b' 作为实参调用 'max' 函数
// 将返回值赋给 'maxValue'
maxValue = max(a, b)
fmt.Printf("Max value between %d and %d is: %d\n", a, b, maxValue)
}

运行此程序将输出:

Max value between 100 and 200 is: 200

Go 的一个独特特性是函数可以返回多个值。这通常用于同时返回一个结果和一个错误状态。

package main
import "fmt"
// 函数返回两个字符串值
func swap(x, y string) (string, string) {
return y, x // 先返回 y,然后返回 x
}
func main() {
firstName := "Alice"
lastName := "Bob"
fmt.Printf("Original: %s %s\n", firstName, lastName)
// 将两个返回值赋给两个变量
swappedLast, swappedFirst := swap(firstName, lastName)
fmt.Printf("Swapped: %s %s\n", swappedFirst, swappedLast)
}

输出:

Original: Alice Bob
Swapped: Bob Alice

Go 允许在函数签名中为返回值命名。这可以提高可读性,特别是对于返回多个值的函数。命名返回值会被初始化为其零值。一个裸露的 return 语句会自动返回命名返回变量的当前值。

package main
import "fmt"
// 包含命名返回值 (result int, err error) 的函数
func divide(dividend, divisor int) (result int, err error) {
if divisor == 0 {
err = fmt.Errorf("cannot divide by zero") // 赋给命名返回值 'err'
// 这里的裸露 return 将返回当前值 (result=0, err=错误)
return
}
result = dividend / divisor // 赋给命名返回值 'result'
// err 保持 nil (其零值)
return // 返回 result 和 err 的当前值
}
func main() {
res1, err1 := divide(10, 2)
if err1 != nil {
fmt.Println("Error:", err1)
} else {
fmt.Println("10 / 2 =", res1)
}
res2, err2 := divide(5, 0)
if err2 != nil {
fmt.Println("Error:", err2)
} else {
fmt.Println("5 / 0 =", res2)
}
}

输出:

10 / 2 = 5
Error: cannot divide by zero

在 Go 中,所有函数实参都是按值传递的。这意味着函数接收的是实参值的副本,而不是对原始变量的引用。

对于基本类型(如 int, float64, string, bool, 结构体),在函数内部对参数所做的修改不会影响函数外部的原始实参变量。

然而,对于切片(slice)、map 和 channel 等引用类型,被复制的值本质上是指向或描述底层数据结构的指针或头部信息。虽然指针/头部信息本身被复制了,但它仍然指向相同的底层数据。因此,在函数内部对切片或 map 的元素所做的修改会在函数外部可见。向切片追加元素可能会改变底层数组,这取决于容量是否发生变化,可能会也可能不会反映在函数外部(这是一个细微之处,通常在切片细节中讲解)。

要明确允许函数修改原始变量(如 int 或 struct),你需要传递该变量的指针(按指针传递,这在技术上仍然是按值传递,只不过传递的值是内存地址)。

传递机制描述与影响
按值传递(默认)函数接收一个副本。内部的修改不会影响原始变量(对于基本类型/结构体)。对于切片/map,对元素的修改会影响原始变量。
按指针传递函数接收内存地址(指针)的副本。通过解引用 (*ptr) 进行的修改会影响原始变量。
概念描述
可变参数函数(Variadic Functions)可以接受同类型可变数量参数的函数。
匿名函数(Anonymous Functions)没有名称的函数,通常立即使用或赋给变量。
闭包(Closures)捕获其周围作用域变量的匿名函数,即使外部函数执行完毕后仍能访问这些变量。
方法(Methods)与特定类型(接收者)关联的函数。在函数名称之前通过接收者参数定义。
延迟函数调用(Deferred Function Calls)使用 defer 关键字会将函数调用推迟到其所在的函数即将返回之前执行。